在结构上调用函数的正确习惯用法是什么?

What is the correct go idiom for calling functions on a struct?

我是新手(来自 python 和 ruby),想知道在结构上调用函数的惯用方式是什么?

主要是我想知道我是否应该使用点运算符来调用函数或使用我的类型作为参数之一。还有用指针好还是不用指针好?

这样可以吗?

package main

import "fmt"

func main() {
    me := Person{firstname: "John", lastname: "Doe", age: 40}
    fmt.Println(me.fullname())
}

type Person struct {
    firstname string
    lastname  string
    age       int
}

func (p Person) fullname() string {
    return p.firstname + p.lastname
}

或者这样

package main

import "fmt"

func main() {
    me := Person{firstname: "John", lastname: "Doe", age: 40}
    fmt.Println(fullname(&me))
}

type Person struct {
    firstname string
    lastname  string
    age       int
}

func fullname(p *Person) string {
    return p.firstname + p.lastname
}

没有一种正确的方法可以做到这一点。了解两者之间的差异并在适当的地方应用它们很重要。此外,您的功能并没有真正的可比性。在一种情况下,您已经定义了一个具有 Person 类型接收者的函数,并且它是按值传递的,这意味着创建了一个实例副本并将其压入堆栈。在另一种情况下,该函数是独立的,您将 Person 引用传递给它。您还可以通过将接收类型设为指针来使用 'instance method' 通过引用传递。

所以在第一种情况下;

func (p Person) fullname() string {
    return p.firstname + p.lastname
}

你像 p.fullname() 一样调用函数,p 的副本被压入堆栈。如果在这个函数中赋值,p不会被修改,作用域中的实例会被修改。所以对于 setter 或任何旨在更改对象状态的函数,这根本不是一个选项。

为了详细说明我所说的备选方案,您可以改为这样做;

func (p *Person) fullname() string {
    return p.firstname + p.lastname
}

仍然允许您在 p.fullname() 这样的实例上调用它,但是,传递给该函数的是 *Person 类型的引用。因此,这将是实现在结构上设置值的函数的典型选择。

现在你的第二个例子;

func fullname(p *Person) string {
    return p.firstname + p.lastname
}

没有接收者,传递一个指针。意思是你这样称呼它 fullname(p)。该函数未导出,因此它仅在声明它的包中可用,在本例中为 main。为了与(可能)更熟悉的语言进行比较,这就像在 C++ 中定义一个函数,而其他两个函数在 'method' 中定义一个函数或在 class 中定义一个函数。另外两个将与 'passing by value' 或 'passing by reference' 进行比较。这通过引用传递,与任何类型都没有关联。

在您的示例中,出于性能原因,我将始终使用 func (p *Person) fullname() string。在我看来,使用按值传递替代方案 func (p Person) fullname() string 的最合适时间是当您想要强制执行不变性时。如果您想在生成一个新对象而不是修改现有对象的地方进行操作(很多集合库都是这样操作的,例如 C# 中的 LINQ 总是生成一个新集合并尝试在查询期间修改该集合将导致异常)那么你可能需要一个值类型的接收器。

希望对您有所帮助。如果这仍然是造成混淆的原因,我可以更新更多信息。