returning value vs pointer in Go constructor

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

returning value vs pointer in Go constructor

问题

在Go语言中构建一个简单对象时,这两种方式有什么区别?

func NewGender(value string) Gender {
    return Gender{strings.TrimSpace(value)}
}

func NewGender(value string) *Gender {
    return &Gender{strings.TrimSpace(value)}
}

这两种方式的区别在于返回值的类型不同。第一种方式返回的是Gender类型的对象,而第二种方式返回的是指向Gender类型对象的指针(*Gender)。具体来说:

  • 第一种方式中,NewGender函数返回一个Gender类型的对象,该对象是通过Gender{strings.TrimSpace(value)}创建的。这种方式会在函数内部创建一个对象,并将其作为返回值返回给调用者。
  • 第二种方式中,NewGender函数返回一个指向Gender类型对象的指针(*Gender),该指针是通过&Gender{strings.TrimSpace(value)}创建的。这种方式会在函数内部创建一个对象,并返回该对象的指针给调用者。

使用哪种方式取决于你的需求和设计。如果你希望在函数内部创建对象并返回,可以使用第一种方式。如果你希望在函数外部创建对象,并将其指针返回,可以使用第二种方式。

英文:

When building a simple object in go, what's the difference between these alternatives?

func NewGender(value string) Gender {
	return Gender{strings.TrimSpace(value)}
}

func NewGender(value string) *Gender {
	return &Gender{strings.TrimSpace(value)}
}

答案1

得分: 24

这个问题非常广泛,它在很大程度上取决于你的API的其他部分。所以在选择一个而不是另一个时,有一些你可能需要考虑的事情(没有特定的顺序):

  • 不必要的指针会给垃圾回收器带来更多的工作。通过返回指针(一个字)而不是结构体(零到多个字),你可能会节省一些时间,但是这些时间可能会在垃圾回收扫描中消耗掉。

  • 指针也可以表示某个东西的存在或不存在,尽管通常最好使用逗号-ok习语来表示,或者返回一个错误。

  • 谈到错误,如果你返回nil和一个错误,错误被忽略的机会就较小(因为nil指针解引用会导致panic)。不过,我认为忽略错误的人并不是你真正需要考虑的因素。

  • 如果你需要某种形式的跟踪(例如,检查构造函数创建的所有实例的可能性),你必须返回一个指针,这样对值的所有更改都对检查器可见。

  • 如果大多数类型的方法都有指针接收器,或者API中的大多数函数接受指向该类型的指针而不是值,那么返回一个指针更符合人体工程学。

英文:

The question is a really broad one, and it depends heavily on the rest of your API. So here are just some things you might need to consider when choosing one over another (in no particular order):

  • Unnecessary pointers lead to more work for the GC. You might win some time by returning a pointer (one word) rather than a struct (zero to many words), but some of that time might later be consumed by the GC scan.

  • Pointers can also signal presence or absence of a thing, although it's generally better to use comma-ok idiom for that, or return an error.

  • Speaking of errors, if you return nil with an error, the chance that the error will be ignored is smaller (because nil pointer dereference will result in panic). Then again, I don't think people who ignore errors are something you should really take into account.

  • If you need some form of tracking (for example, a possibility to inspect all instances ever created by your constructor), you have to return a pointer, so that all changes to the value would be visible to the inspector.

  • If most of the type's methods have a pointer receiver, or most functions in the API accept pointers to the type rather than values, it's more ergonomic to return a pointer.

答案2

得分: 4

第一个返回的项是一个值,第二个是一个指针。这个指针的工作方式很像C或C++中的指针,只是它指向的值会像在C#或Java中一样进行垃圾回收。Go语言在这两种范式之间提供了一种妥协。指针是显式和可见的,但它仍然进行垃圾回收。以下是一些明显的区别:

1)如果你将值传递给一个方法或者它是接收器(这经常让人困惑),你将无法修改它的值,它将像其他语言中的“按值传递”一样,你修改的是一个副本。是的,即使是方法func (g Gender) ChangeGender()也不会改变你调用它的对象的性别。

2)你只能调用为该类型定义的方法。例如,如果你有g *Gender并且你想调用上面的ChangeGender方法,你将无法直接调用,除非对指针进行解引用,就像在C/C++中一样。反过来也是如此。

3)传递引用是传递值的替代方法,你可能知道它是如何工作的,但以防你不知道,我来解释一下。如果你有一个方法func (g *Gender) ModifyGender()和一个实例g := &Gender{},那么当调用ModifyGender时,一个指向操作系统字大小的指针将作为参数推送到堆栈上,而不是值本身,方法将使用该地址来修改那里的内存,在控制返回给调用者时保持更改。如果你有一个在具有大内存占用的对象上工作的方法,这可以极大地提高性能。

我不打算谈论性能。这应该为你提供足够的信息来考虑它在你的应用程序中的工作方式。

英文:

The first item returned is a value, the second is a pointer. The pointer works much like a pointer in C or C++ only the value it points to is garbage collected like in C# or Java. Go offers something of a compromise between those two paradigms. The pointer is explicit and exposed, however it's still garbage collected. Here are a few somewhat obvious differences;

  1. If you pass the value to a method or it is the receiver (which is often confusing) you will not be able to modify it's value, it will be a 'pass by value' like in the other languages where you are modifying a copy. Yes, even the method func (g Gender) ChangeGender() will not change the gender of the object you call it on. https://play.golang.org/

  2. You can only call methods defined for the type. For example if you have g *Gender and you want to call the ChangeGender method above you will not be able to without dereferencing your pointer, just like in C/C++. The opposite is true going in the opposite direction.

  3. The alternative to passing by value is by reference, you probably know how that works but in case you don't I'll explain. If you have a method func (g *Gender) ModifyGender() and an instance g := &Gender{} then when calling ModifyGender a pointer of the OS' word size will get pushed onto the stack as an argument rather than the value itself and the method will use that address to modify the memory there, persisting changes when control returns to the caller. This can vastly improve performance if you have method which work on objects that have a large memory foot print.

I'm not going to speak to performance. That should give you enough information to consider how it works in your application.

答案3

得分: 3

正如@evanmcdonnal所说,选择与您如何使用结构相关。

  • 如果您的结构仅用于提供信息,并且不打算在以后对其进行更改,请保持简单并返回一个值(例如自定义错误结构)。如果您的结构非常庞大,但您不打算对其进行更改,请使用指针(例如RGB矩阵)。

  • 如果您计划在程序工作流程中更改结构,请使用指针。

英文:

Just as @evanmcdonnal said the choise is more related to how you are going to use the struct.

  • If your struct is informative only and you are not planning to mutate it later, keep it simple and return a value (e.g. a Custom Error struct), in case your struct is really large but you do not plan to mutate it use a pointer (e.g. a RGB matrix)

  • If you plan to mutate your struct durign your program workflow use a pointer.

huangapple
  • 本文由 发表于 2015年8月25日 23:41:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/32208363.html
匿名

发表评论

匿名网友

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

确定