为什么Go的构造函数会返回地址?

时间:2015-08-11 03:42:43

标签: pointers go constructor

我知道Go没有任何构造函数,并且New func用于它的位置,但根据this example

func NewFile(fd int, name string) *File {
  if fd < 0 {
    return nil
  }
  f := File{fd, name, nil, 0}
  return &f
}

他们总是返回&f。为什么只是简单地返回File是不够的?

更新

我已经尝试为简单的结构返回创建的对象,这很好。所以,我想知道返回一个地址是否是构造函数的标准方法。

感谢。

2 个答案:

答案 0 :(得分:14)

如上所述,是的,规范允许您返回值(作为非指针)或指针。这只是你必须做出的决定。

何时返回指针?

通常,如果返回的值作为指针“更有用”。什么时候更有用?

例如,如果它有许多带指针接收器的方法。是的,您可以将返回值存储在变量中,因此它是可寻址的,您仍然可以调用其具有指针接收器的方法。但是如果立即返回指针,则可以“链接”方法调用。见这个例子:

type My int

func (m *My) Str() string { return strconv.Itoa(int(*m)) }

func createMy(i int) My { return My(i) }

现在写道:

fmt.Println(createMy(12).Str())

会导致错误:cannot call pointer method on createMy(12)

但如果你返回一个指针就行了:

func createMy(i int) *My { return (*My)(&i) }

此外,如果将返回的值存储在不可寻址的数据结构中(例如map),则无法通过索引映射来调用值的方法,因为映射的值不可寻址。

请参阅此示例:My.Str()具有指针接收器。所以如果你试着这样做:

m := map[int]My{0: My(12)}
m[0].Str() // Error!

你不能因为“不能取m[0]的地址。但是以下工作:

m := map[int]*My{}
my := My(12)
m[0] = &my // Store a pointer in the map

m[0].Str() // You can call it, no need to take the address of m[0]
           // as it is already a pointer

指针有用的另一个例子是它是一个“大”结构,它将被传递很多http.Request是一个光辉的榜样。它很大,通常会传递给其他处理程序,它有指针接收器的方法。

如果返回一个指针,通常会建议返回的值如果存储并作为指针传递则更好。

答案 1 :(得分:0)

指针接收器接受指针和值类型,只要它与数据类型匹配即可。

type User struct {
    name  string
    email string
    age   int
}

// NewUserV returns value ... ideally for a User we should not be 
// returning value
func NewUserV(name, email string, age int) User {
    return User{name, email, age}
}

// NewUserP returns pointer ...
func NewUserP(name, email string, age int) *User {
    return &User{name, email, age}
}

// ChangeEmail ...
func (u *User) ChangeEmail(newEmail string) {
    u.email = newEmail
}

func main() {
    // with value type
    usr1 := NewUserV("frank", "frank@camero.com", 22)
    fmt.Println("Before change: ", usr1)
    usr1.ChangeEmail("frank@gmail.com")
    fmt.Println("After change: ", usr1)

    // with pointer type
    usr2 := NewUserP("john", "john@liliput.com", 22)
    fmt.Println("Before change: ", usr2)
    usr2.ChangeEmail("john@macabre.com")
    fmt.Println("After change: ", usr2)

}

除了icza提到的关于传递的大型结构的内容之外。指针值可以说是指针语义在起作用,并且使用特定类型的人不应复制指针共享的值。

如果您查看File或http类型的结构,它会维护该值唯一的通道或某些其他指针类型。复制值(由指针提供给您)将导致难以发现错误,因为复制的值可能最终会写入或读取原始值的指针类型。