# #go
Вопрос:
У меня есть некоторые вопросы об интерфейсах, особенно когда эти интерфейсы передаются между функциями.
Я понимаю, что интерфейсы выполняются неявно, что означает, что следующий код действителен:
type itemX struct {}
func (x *itemX) Do() string {
return "itemX"
}
type Itf interface {
Do() string
}
func test(i Itf) string {
return i.Do()
}
func main() {
x := new(itemX)
str := test(x) // valid, since x implicitly satisfies Itf
}
Однако не так ясно, что происходит или на что похож контракт типа, когда я начинаю передавать интерфейсы между функциями. Пример:
// itemX, Itf, and test have the same declaration as the above snippet
func returnsItf(i Itf) Itf {
return i
}
func returnsTypeAssertedX(i Itf) Itf {
return i.(*itemX)
}
func takeItf(i Itf) {}
func takeX(x *itemX) {}
func main() {
x := new(itemX)
var i Itf = x
a := returnsItf(i) // returns type Itf
_ = takeItf(a) // no error
b := returnsTypeAssertedX(i)
_ = takeItf(b) // no error, since *itemX implements Itf
_ = takeX(b) // error, cannot use b (type Itf as *itemX)
}
Похоже, существует какое-то скрытое поведение, когда интерфейс передается в качестве возврата функции. Если возвращаемое значение равно *itemX
и тип равен Itf
, то возвращаемое значение преобразуется в Itf
до завершения фрейма функции.
Таким образом, эта неявная проверка (конкретная -> интерфейс, если тип-интерфейс) выполняется >дважды за вызов функции:
- в начале каждого вызова функции,
- и в самом конце.
Правильно ли я понимаю это неявное преобразование?
Ответ №1:
Интерфейс-это тип данных, содержащий два элемента: тип базового объекта и указатель на этот объект. Итак, когда вы используете неинтерфейсный тип в контексте, которому нужен интерфейс, компилятор создает тип интерфейса из этого значения и использует его.
func returnsTypeAssertedX(i Itf) Itf {
return i.(*itemX)
}
В приведенной выше функции он сначала утверждает, что переданный аргумент имеет требуемый тип, а затем преобразует базовое значение аргумента обратно в интерфейс.
b := returnsTypeAssertedX(i)
_ = takeX(b)
Вышесказанное не сработает, потому b
что является interface{}
и takeX
требует *itemX
. Однако это сработало бы:
takeX(b.(*itemX))