Ця помилка часу компіляції виникає при спробі призначити або передати (або перетворити) конкретний тип типу інтерфейсу; і сам тип не реалізує інтерфейс, тільки покажчик типу .
Давайте подивимось приклад:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Stringer
Тип інтерфейсу має тільки один метод: String()
. Будь-яке значення, яке зберігається у значенні інтерфейсу, Stringer
повинно мати цей метод. Ми також створили a 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 ):
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 ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Крім того, що б ми не вставили ( MyType
або *MyType
), якщо ми будемо використовувати вказівник *MyType2
, він завжди буде працювати (спробуйте на майданчику Go ):
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 ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Намагаючись перетворити замість типової ствердження, ми отримуємо помилку часу компіляції, про яку ми говоримо:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
", або жодними . Це так? Чи можна змішувати різні типи "функцій членів", наприклад,func (m *MyType)
&func (m MyType)
?