Проблема с реализацией интерфейсов в Go
недавно начал изучать Go и столкнулся с такой проблемой. Первая структура имеет метод с value receiver, а вторая структура с point receiver. Обе структуры реализуют интерфейс. В первом случае я присваиваю интерфейсу структуру по значению и по ссылке. Ошибки почему-то не выдает, хотя метод первой структуры имеет рисивер на значение, а не на адрес. Но во втором случае, если я пытаюсь передать вторую структуру не по ссылке, а по значению, то возникает ошибка, т.к интерфейс не реализуется. Сильно запутался и не могу понять, почему в первом случае интерфейс реализуется при передачи структуры и по значению, и по ссылке.
package main
import "fmt"
type ifRealization interface {
init()
}
type firstStruct struct{}
func (f firstStruct) init() {
fmt.Printf("Init; Type: %T Value: %v", f, f)
}
type secondStruct struct{}
func (f *secondStruct) init() {
fmt.Printf("Init; Type: %T Value: %v", f, f)
}
func main() {
var first ifRealization = firstStruct{}
var second ifRealization = &firstStruct{} // Ошибки нет
first.init()
second.init()
var f ifRealization = secondStruct{} //Type does not implement 'ifRealization' as the 'init' method has a pointer receiver
var s ifRealization = &secondStruct{} //Ошибки нет
}
Ответы (1 шт):
Интерфейс - это перечень методов с заданными сигнатурами. Тип T реализует интерфейс, если набор методов (method set) типа T включает в себя множество методов интерфейса.
Теперь о том, как вычисляется method set: https://go.dev/ref/spec#Method_sets
- Если
Tявляется не-указателем, то набор методов складывается из методов для типаT, то есть методов, реализованных какfunc (obj T) Method(...) - Если
Tявляется указателем, то естьT = *T1, то набор методов складывается из методов для типа*T1и самого типаT1, то есть методов, реализованных какfunc (obj *T1) Method(...)и какfunc (obj T1) Method(...)
Для не-указателя firstStruct вы реализовали метод func (f firstStruct) init() {}. В соответствии с правилами вычисления method set метод init входит в набор методов как для типа firstStruct, так и для типа *firstStruct. Именно поэтому переменная second, являющаяся указателем, реализует интерфейс ifRealization.
В случае secondStruct вы реализовали метод init для указателя: func (f *secondStruct) init(). Следовательно, этот метод входит в method set для типа *secondStruct, но не входит в набор методов типа secondStruct. Как следствие, переменная s реализует интерфейс, а переменная f - нет.