切片与类型别名作为方法接收器

Slice vs type alias as the method receiver

提问人:z11i 提问时间:2/20/2020 最后编辑:iczaz11i 更新时间:2/20/2020 访问量:4231

问:

我有一个带有方法的结构体:Fooprint

type Foo struct {
    Bar string
}
func (f Foo) print() {
    fmt.Println(f.Bar)
}

如果我想打印一个切片,规范的方法可能是编写一个循环,并有一个函数来封装它:Foofor

func printFoos(fs []Foo) {
    for _, f := range fs {
        f.print()
    }
}
printFoos([]Foo{})

来自OOP背景,我发现这种方法有点不吸引人。

我想做的是与:printFoos[]Foo

// Invalid Go code
func (fs []Foo) print() {
    for _, f := range fs {
        f.print()
    }
}

上述方法不起作用,因为在 Go 中,未命名的类型不能用作方法接收器,正如此 Google Group 线程中所讨论的那样。

为了规避它,可以写成:

type Foos []Foo
func (fs Foos) print() {
    for _, f := range fs {
        f.print()
    }
}

要使用它,我必须显式声明类型为 ,所以我仍然不能使用Foosprint[]Foo

fs := []Foo{}
fs.print() // error
var fss Foos = fs
fss.print()

我感到困惑的是,在上面的代码中,它们显然属于同一类型,因为我可以毫无错误地分配给它们。但是,我们不能简单地使用并让 Go 聪明地进行转换。fssfsfsfssfs.print()

为什么会这样?

完整的代码可以在 playground 上找到。

Go 方法 类型

评论

0赞 Adrian 2/20/2020
因为声明在 上,而不是在 上。printFoos[]Foo

答:

11赞 icza 2/20/2020 #1

我感到困惑的是,在上面的代码中,它们显然属于同一类型,因为我可以毫无错误地分配给它们。fssfsfsfss

你得出了错误的结论。具有相同的类型不是可分配性的必要条件。

fsshas type 和 has type ,一种未命名的切片类型。确实,它们具有相同的基础类型,这就是为什么您可以分配给 ,在此可分配性规则中介绍:Foosfs[]Foofsfss

如果满足以下条件之一,则值可分配给类型为(“可分配给”)的变量xTxT

  • ...
  • x的类型,并且具有相同的基础类型,并且至少有一个或不是定义的类型VTVT

方法绑定到具体类型。因此,该方法不适用于其他类型的值,包括 。Foos.print()[]Foo

但是你不需要创建一个变量来调用该方法,你可以简单地使用类型转换

Foos(fs).print()

此转换不会更改内存布局,只会更改类型,因此安全高效。我们仅使用它来访问具有相同基础类型的类型的方法。

评论

0赞 Shuzheng 11/9/2021
VT 中的至少一个不是定义的类型是什么意思?不是两种类型都定义了吗?IOW,也是一个定义的类型。type Foos Foo[]
0赞 icza 11/9/2021
@Shuzheng 是定义的类型,但 的参数,这里的 type 是 ,它不是定义的类型。这意味着 的类型可以赋值给 类型的变量,因此可以作为 的参数传递,因为可赋值性规则适用(VT 中的至少一个不是定义类型” – 不是定义类型)。FoosprintFoos(fs []Foo)fs[]FooFoos[]Foofs[]Foo