3 ответов:
эта ошибка времени компиляции возникает при попытке назначить или передать (или преобразовать) a б тип к типу интерфейса; и сам тип не реализует интерфейс, только указатель на тип.
давайте посмотрим пример:
type Stringer interface { String() string } type MyType struct { value string } func (m *MyType) String() string { return m.value }The
Stringerтип интерфейса имеет только один метод:String(). Любое значение, которое хранится в интерфейсе valueStringerдолжны иметь этот метод. Мы также создалиMyType, и мы создали методMyType.String()С указатель приемник. Это означаетString()способ метод set на*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.другими словами: если мы вставляем тип без указателя, набор методов не указателя embedder получает только методы с приемниками без указателя (из встроенного типа).
если мы вставим указатель тип, способ набора номера-указатель эмбеддер возвращает методы с приемниками указателей и без указателей (из внедренного типа).
если мы используем значение указателя на embedder, независимо от того, является ли встроенный тип указателем или нет, набор методов указателя на embedder всегда получает методы как с указателем, так и с приемниками без указателя (из встроенного типа).
Примечание:
есть очень похожий случай, а именно когда у вас есть значение интерфейс, который обертывает значение
MyType, а ты попробуй тип assert другое значение интерфейса из него,Stringer. В этом случае утверждение не будет выполняться по причинам, описанным выше, но мы получим немного другую ошибку времени выполнения:m := MyType{value: "something"} var i interface{} = m fmt.Println(i.(Stringer))Runtime panic (попробуйте его на Go Playground):
panic: interface conversion: main.MyType is not main.Stringer: missing method Stringпытаясь преобразовать вместо типа assert, мы получаем ошибку времени компиляции, о которой мы говорим:
m := MyType{value: "something"} fmt.Println(Stringer(m))
чтобы сократить его, скажем, у вас есть этот код, и у вас есть интерфейс загрузчика и WebLoader, который реализует этот интерфейс.
package main import "fmt" // Loader defines a content loader type Loader interface { Load(src string) string } // WebLoader is a web content loader type WebLoader struct{} // Load loads the content of a page func (w *WebLoader) Load(src string) string { return fmt.Sprintf("I loaded this page %s", src) } func main() { webLoader := WebLoader{} loadContent(webLoader) } func loadContent(loader Loader) { loader.Load("google.com") }так что этот код даст вам эту ошибку времени компиляции
./главный.go: 20: 13: нельзя использовать webLoader (тип WebLoader) в качестве загрузчика типа в аргументе к loadContent: WebLoader не реализует Loader (метод Load имеет приемник указателя)
так что вам нужно только изменить
webLoader := WebLoader{}следующее:webLoader := &WebLoader{}так почему это будет исправить, потому что вы определяете эту функцию
func (w *WebLoader) Loadчтобы принять приемник указателя. Для получения дополнительных объяснений, пожалуйста, прочитайте @icza и @karora answers
другой случай, когда я видел, что такое происходит, - это если я хочу создать интерфейс, где некоторые методы будут изменять внутреннее значение, а другие-нет.
type GetterSetter interface { GetVal() int SetVal(x int) int }что-то, что потом реализует этот интерфейс может быть так:
type MyTypeA struct { a int } func (m MyTypeA) GetVal() int { return a } func (m *MyTypeA) SetVal(newVal int) int { int oldVal = m.a m.a = newVal return oldVal }таким образом, тип реализации, скорее всего, будет иметь некоторые методы, которые являются приемниками указателей, а некоторые-нет, и поскольку у меня есть довольно много этих различных вещей, которые являются GetterSetters, я хотел бы проверить мои тесты, что они все делают ожидается.
если бы я сделал что-то вроде этого:
myTypeInstance := MyType{ 7 } ... maybe some code doing other stuff ... var f interface{} = myTypeInstance _, ok := f.(GetterSetter) if !ok { t.Fail() }тогда я не получу вышеупомянутую ошибку" X не реализует Y (метод Z имеет приемник указателя) " (поскольку это ошибка времени компиляции), но я будет у вас плохой день, преследующий именно то, почему мой тест терпит неудачу...
вместо этого я должен убедиться, что я делаю проверку типа с помощью указателя, например как:
var f interface{} = new(&MyTypeA) ...или:
myTypeInstance := MyType{ 7 } var f interface{} = &myTypeInstance ...тогда все довольны тестами!
но подождите! В моем коде, возможно, у меня есть методы, которые принимают GetterSetter где-то:
func SomeStuff(g GetterSetter, x int) int { if x > 10 { return g.GetVal() + 1 } return g.GetVal() }если я вызову эти методы изнутри другого метода типа, это приведет к ошибке:
func (m MyTypeA) OtherThing(x int) { SomeStuff(m, x) }любой из следующих вызовов будет работать:
func (m *MyTypeA) OtherThing(x int) { SomeStuff(m, x) } func (m MyTypeA) OtherThing(x int) { SomeStuff(&m, x) }
Comments