Эта ошибка времени компиляции возникает, когда вы пытаетесь назначить или передать (или преобразовать) конкретный тип в тип интерфейса; а сам тип не реализует интерфейс, только указатель на тип .
Давайте посмотрим на пример:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Stringer
Тип интерфейса имеет только один метод: String()
. Любое значение, которое хранится в значении интерфейса, Stringer
должно иметь этот метод. Мы также создали MyType
, и мы создали метод MyType.String()
с указателем приемника. Это означает, что String()
метод находится в наборе методов *MyType
типа, но не в методеMyType
.
Когда мы пытаемся присвоить значение MyType
переменной типа Stringer
, мы получаем соответствующую ошибку:
m := MyType{value: "something"}
var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
// MyType does not implement Stringer (String method has pointer receiver)
Но все в порядке , если мы пытаемся присвоить значение типа *MyType
на Stringer
:
s = &m
fmt.Println(s)
И мы получаем ожидаемый результат (попробуйте на Go Playground ):
something
Итак, требования для получения этой ошибки времени компиляции:
- Значение присваиваемого (или переданного или преобразованного) конкретного типа без указателя
- Тип интерфейса, назначаемый (или переданный, или преобразованный в)
- Конкретный тип имеет требуемый метод интерфейса, но с указателем приемника
Возможности решения вопроса:
- Необходимо использовать указатель на значение, чей набор методов будет включать метод с получателем указателя
- Или тип получателя должен быть изменен на не указатель , поэтому набор методов конкретного типа без указателя также будет содержать метод (и, таким образом, удовлетворять интерфейсу). Это может или не может быть жизнеспособным, так как если метод должен изменить значение, приемник без указателя не вариант.
Структуры и встраивание
При использовании структур и встраивания , часто не «вы» реализуют интерфейс (предоставляют реализацию метода), а тип, который вы встраиваете в свой struct
. Как в этом примере:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Опять же, ошибка времени компиляции, потому что набор методов MyType2
не содержит String()
метод встроенного MyType
, только набор методов *MyType2
, поэтому работает следующее (попробуйте на Go Playground ):
var s Stringer
s = &m2
Мы также можем заставить его работать, если встраиваем *MyType
и используем только не указатель MyType2
(попробуйте на Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Кроме того, что бы мы ни встраивали (или, MyType
или *MyType
), если мы используем указатель *MyType2
, он всегда будет работать (попробуйте на Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Соответствующий раздел из спецификации (из раздела Типы структур ):
При заданном типе структуры S
и названном типе T
продвигаемые методы включаются в набор методов структуры следующим образом:
- Если
S
содержит анонимное поле T
, наборы методов S
и *S
оба включают повышенные методы с получателем T
. Набор методов *S
также включает в себя повышенные методы с приемником *T
.
- Если
S
содержит анонимное поле *T
, наборы методов S
и *S
оба включают повышенные методы с получателем T
или *T
.
Другими словами: если мы встраиваем тип без указателя, набор методов устройства для вставки без указателя получает только методы с получателями без указателя (из встроенного типа).
Если мы встраиваем тип указателя, набор методов устройства для вставки без указателя получает методы как с указателями, так и с получателями без указателя (из встроенного типа).
Если мы используем значение указателя для устройства для внедрения, независимо от того, является ли встроенный тип указателем или нет, набор методов указателя на устройство для внедрения всегда получает методы как с указателями, так и с получателями без указателей (из встроенного типа).
Примечание:
Существует очень похожий случай, а именно, когда у вас есть значение интерфейса, которое упаковывает значение MyType
, и вы пытаетесь напечатать другое значение интерфейса из него Stringer
,. В этом случае утверждение не будет выполнено по причинам, описанным выше, но мы получим немного другую ошибку времени выполнения:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Паника во время выполнения (попробуйте на Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Пытаясь преобразовать вместо типа assert, мы получаем ошибку времени компиляции, о которой мы говорим:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
», либо ни одного . Это так? Могу ли я смешивать различные типы «функций-членов», напримерfunc (m *MyType)
&func (m MyType)
?