Как типы интерфейсов обрабатываются между функциями?

# #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))