接口类型在函数之间如何处理?

huangapple go评论89阅读模式
英文:

How are interface types handled between functions?

问题

我对接口有一些问题,特别是当这些接口在函数之间传递时。

我知道接口可以隐式地满足,也就是说以下代码是有效的:

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)  // 有效,因为x隐式满足Itf接口
}

然而,当我开始在函数之间传递接口时,发生了什么以及类型约定是什么就不那么清楚了。以下是一个例子:

// itemX、Itf和test的声明与上面的代码片段相同

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)   // 返回类型为Itf的值
    _ = takeItf(a)       // 没有错误

    b := returnsTypeAssertedX(i)
    _ = takeItf(b) // 没有错误,因为*itemX实现了Itf接口
    _ = takeX(b)   // 错误,无法将b(类型为Itf)用作*itemX类型
}

当一个接口作为函数返回时,似乎有一些隐藏的行为。如果返回类型是*itemX而函数的类型是Itf,则在函数框架终止之前,返回值会被转换为Itf类型。

因此,这种隐式检查(具体类型 -> 接口类型)在每次函数调用时都会进行两次

  • 在每次函数调用开始时,
  • 和在结束时。

对于这个隐式转换的理解是否正确?

英文:

I have some questions about interfaces, especially when these interfaces are passed between functions.

I understand that interfaces are satisfied implicitly, meaning the following code is valid:

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
}

However, it is not so clear what happens or what the type contract is like when I start passing interfaces between functions. An example:

// 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)
}

There seems to be some hidden behavior when an interface is passed out as a function return. If the return is *itemX and type is Itf, the return is transformed into Itf before the function frame is terminated.

So, this implicit check (concrete -> interface if type is interface) is done twice per function call:

  • at the start of each function call,
  • and at the end.

Is my understanding of this implicit transformation correct?

答案1

得分: 1

接口是一种具有两个成员的数据类型:底层对象的类型和指向该对象的指针。因此,当您在需要接口的上下文中使用非接口类型时,编译器会从该值构造一个接口类型,并使用它。

在上面的函数中,它首先对传入的参数进行类型断言,确保其为所需的类型,然后将参数的底层值转换回接口。

func returnsTypeAssertedX(i Itf) Itf {
    return i.(*itemX)
}

上述代码将无法正常工作,因为b是一个interface{}类型,而takeX需要一个*itemX类型。然而,以下代码可以正常工作:

takeX(b.(*itemX))
英文:

An interface is a data type that has two members: The type of the underlying object, and a pointer to that object. So, wen you use a non-interface type in a context that needs an interface, the compiler constructs an interface type from that value, and uses that.

func returnsTypeAssertedX(i Itf) Itf {
    return i.(*itemX)
}

In the above function, it first type-asserts that the passed in argument is of the required type, and then converts the underlying value of the argument back to an interface.

b := returnsTypeAssertedX(i)
_ = takeX(b) 

The above will not work, because b is an interface{}, and takeX requires an *itemX. However, this would work:

takeX(b.(*itemX))

huangapple
  • 本文由 发表于 2021年9月13日 22:23:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/69164378.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定