为什么在Go语言中进行赋值会创建一个副本?

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

Why does assignment in Go create a copy?

问题

我会稍微澄清一下问题。我已经(几乎完全)阅读了Go语言的规范、常见问题、Effective Go,当然还有Go之旅。

我知道Go是一种“按值传递”的语言,甚至成功地推理出了这种行为并理解了所有的含义。

在Go中,所有的赋值操作都会创建副本。在某些情况下,它只是一个值,在某些情况下,它是一个指针。对于某些数据结构来说,情况会有些棘手,因为整个结构会被复制,可能包含对另一个数据结构的隐式指针。

问题是:语言规范中有什么明确说明赋值操作总是创建副本的内容?

我觉得一旦你理解了Go中没有引用这一点,甚至不需要提到这一点,但是规范中关于赋值语句的部分甚至没有提到按值传递的语义。

我觉得文档中一定有描述这种行为的详细说明,但由于缺乏一些基础的误解,我没有意识到解释就在那里。

英文:

I will clarify the question a bit. I have read (almost completely) the Go specification, FAQ, Effective Go, and, of course, Tour of Go.

I know that Go is a "pass by value" language and even managed to reason about this behavior and understand all the implications.

All assignments in Go also create copies. In some cases, it's just a value, in some -- a pointer. For some data structures, it's a bit trickier in that the whole structure is copied and might include an implicit pointer to another data structure.

The question is: what in the language specification says explicitly that assignments always create copies?

I feel like it doesn't even need to be mentioned once you understand that there are no references in Go, but the section on assignment statements in the specification doesn't even mention the pass-by-value semantics.

I feel like there must be something in the documentation that describes the behavior in detail, and I, due to lack of some foundational misunderstanding, fail to realize the explanation is there.

答案1

得分: 3

在语言规范中并没有明确说明赋值操作总是创建副本,但你可以从《变量》这一章节中推断出这一点。该章节还很好地解释了函数签名的情况:

变量声明或者函数参数和结果的签名都会为命名变量保留存储空间

如果存储空间被保留,当你将一元表达式的结果赋值给它时,比如另一个变量,那么它必须是一个副本,否则就会存在内存别名问题。这也是Dave Cheney在《Go中不存在按引用传递》一文中所讨论的内容。

与C++不同,Go程序中定义的每个变量都占据着唯一的内存位置。

这还有一个重要的含义,即零值。如果在变量声明时没有提供初始化表达式,为其保留的存储空间将被赋予默认的零值作为初始值。

英文:

> What in the language specification says explicitly that assignment always creates copies?

Nothing explicit, but you can maybe deduce this from Variables, which nicely addresses also the case of function signatures:

> A variable declaration or, for function parameters and results, the signature of a function declaration or function literal reserves storage for a named variable.

If storage is reserved, when later you assign the result of a unary expression to it — e.g. another variable —, then it must be a copy, otherwise you would have memory aliasing. Which is what Dave Cheney is talking about in There is no pass-by-reference in Go.

> Unlike C++, each variable defined in a Go program occupies a unique memory location.

This also has one more important implication, which is the zero value. If you don't provide an expression to initialize a variable in its declaration, the storage reserved for it is given the zero value as default value.

答案2

得分: 2

不详细讨论的话,这些摘录来自规范应该能提供一些明确的解释:
> 变量是用于存储值的存储位置。
>
> 变量的值[...] 是最近分配给变量的值。

在语言层面上,定义值的“复制”并不是真正必要的。我们通常理解的复制的重要含义是,修改“复制到”的值不会改变“复制自”的值*。这个特性可以从上述引文中推断出来。

*(这里需要注意的是,“值”和“数据结构”并不是同一回事。一个数据结构可能由多个值组成。)

英文:

Without going into too much detail, these excerpts from the spec should provide some clarity:
> A variable is a storage location for holding a value.
>
> A variable's value [...] is the most recent value assigned to the variable.

At the language level, defining "copying" of values isn't really necessitated. The important implication of copying as we commonly understand it, is that modifying the "copied to" value will not alter the "copied from" value *. This property can be inferred from the above quotations.

  • (important to note here that "value" and "data structure" are not the same thing. A data structure may be comprised of multiple values.)

答案3

得分: -1

这里实际上明确讨论了这个问题:

https://golang.org/ref/spec#Calls

特别是:

> 在它们被评估之后,调用的参数按值传递给函数,并且被调用的函数开始执行。函数的返回参数在函数返回时按值传递回调用者。

关于赋值:我所了解的大多数语言中,所有的赋值操作都会创建值的副本(请参考评论中的Python异常)。将右侧的值的副本赋给左侧。如果右侧是一个指针,则将该指针的副本赋给左侧。

英文:

The spec actually explicitly talks about this here:

https://golang.org/ref/spec#Calls

In particular:

> After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the caller when the function returns.

About assignments: All assignments in most of the languages I know of create copies of values (see Python exception in the comments). A copy of the value on the RHS is assigned to the LHS. If the RHS is a pointer, then a copy of that pointer is assigned to the LHS.

huangapple
  • 本文由 发表于 2021年8月8日 04:36:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/68695937.html
匿名

发表评论

匿名网友

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

确定