Эта ошибка времени компиляции возникает, когда вы пытаетесь назначить или передать (или преобразовать) конкретный тип в тип интерфейса; а сам тип не реализует интерфейс, только указатель на тип .
Давайте посмотрим на пример:
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)
?MyType
, если вы не можете перейтиMyType
на использование методов значения. Я пробовал что-то подобное,(&i).(*Stringer)
но это не работает. Это вообще возможно?x := i.(MyType)
, и затем вы можете вызывать методы с получателем указателя, напримерi.String()
, который является сокращением для(&i).String()
которого успешно, потому что переменные являются адресуемыми. Но метод указателя, изменяющий значение (указанное значение), не будет отражен в значении, заключенном в значение интерфейса, поэтому он не имеет особого смысла.*T
не включены в набор методов,S
потому что ониS
могут быть не адресуемыми (например, возвращаемое значение функции или результат индексации карты), а также потому, что часто присутствует / принимается только копия, и если получение ее адреса разрешено, метод с указателем получатель может только изменить копию (путаница, поскольку вы предполагаете, что оригинал изменен). Посмотрите этот ответ для примера: Использование отражения SetString .Короче говоря, допустим, у вас есть этот код, и у вас есть интерфейс Loader и WebLoader, который реализует этот интерфейс.
Так что этот код даст вам эту ошибку времени компиляции
Так что вам нужно только перейти
webLoader := WebLoader{}
на следующее:Так почему это будет исправлено, потому что вы определяете эту функцию,
func (w *WebLoader) Load
чтобы принимать указатель на приемник. Для более подробного объяснения, пожалуйста, прочитайте ответы @icza и @karora.источник
Другой случай, когда я видел, что такое происходит, - это если я хочу создать интерфейс, в котором некоторые методы изменят внутреннее значение, а другие - нет.
То, что затем реализует этот интерфейс, может выглядеть так:
Таким образом, тип реализации, скорее всего, будет иметь некоторые методы, которые являются получателями указателей, а некоторые - нет, и, поскольку у меня есть множество этих различных вещей, которые являются GetterSetters, я хотел бы проверить в своих тестах, что они все выполняют ожидаемые.
Если бы я сделал что-то вроде этого:
Тогда я не получу вышеупомянутую ошибку «X не реализует Y (у метода Z есть указатель на приемник)» (поскольку это ошибка времени компиляции), но у меня будет плохой день, чтобы выяснить, почему мой тест терпит неудачу ... ,
Вместо этого я должен убедиться, что я делаю проверку типа с помощью указателя, например:
Или:
Тогда все довольны тестами!
Но ждать! В моем коде, возможно, у меня есть методы, которые где-то принимают GetterSetter:
Если я вызову эти методы из другого метода типа, это сгенерирует ошибку:
Любой из следующих вызовов будет работать:
источник