哪些类型在Google Go语言中是可变的和不可变的?

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

Which types are mutable and immutable in the Google Go Language?

问题

在Google Go中,我了解到字符串是不可变的,好的,但是整数呢?其他类型呢?作为一个稍微年长的程序员,我更喜欢可变性,尽管我知道不可变性的好处,但我更喜欢冒险。

了解哪些类型是可变的或不可变的将非常有帮助。


更新,我最关心的是根据类型的可变性或不可变性而产生的实际问题。就像在Java中的典型示例一样,如果你在循环中创建一个字符串并循环10,000次,你将得到10,000个创建的字符串,然后稍后被垃圾回收。这实际上在我曾经工作过的一家公司的一个项目中是一个严重的问题。

那么问题是,Go的不可变性在某些情况下是否会导致相同的问题?

它会影响你如何处理变量。(或者我认为会影响)


再次更新,我还关心其他实际问题。知道某个东西是不可变的意味着我可以编写并行的代码,并且对对象的一个引用的更新不应该更新其他引用。然而有时我希望做一些危险的事情,我想要可变性。

这些是可变性与不可变性的后果,它们会影响我编写代码的方式。

英文:

In Google Go, I read that Strings are immutable, ok but are int's?
What about other types? As a slightly older programmer I prefer mutability even though I know the benefits of immutability, I prefer to live dangerously.

Know what types are mutable or immutable would be very helpful.


Update, what I am mostly concerned about is the practical issues depending upon the type being mutable or immutable. As in the typical example in Java, if you create a String in a loop and loop for 10,000 times, you will get 10,000 String's created which are then later garbage collected. This has actually been a serious issue in a project in a company I worked at.

The the question is, does Go's Immutability in some cases cause the same problem?

It affects how you should treat the var. (or I assume it does).


Update again, I am also concerned about other practical concerns. Knowing that something is immutable means that I can write code which is parallel and updates to one reference of the object should not update the other references. However sometimes I wish to do dangerous things, I want mutability.

These are consequences of mutability vs immutability and affect how I can write the code.

答案1

得分: 49

不要担心 - 如果你真的想的话,Go会让你自己踩到自己的脚上 哪些类型在Google Go语言中是可变的和不可变的?

Go不像Erlang,这可能是你在提问时想到的。

x := 1
x = 2

分配一个变量x,值为1,然后将其重新赋值为2 - 这里不会分配额外的内存。

正如你所指出的,字符串是不可变的,所以进行字符串操作可能会导致复制。如果你发现你想对字符数据进行原地修改,你可能会想通过bytes包来操作[]byte类型的变量。

Russ Cox关于这个问题的帖子应该能回答你关于基本数据结构的大部分问题:http://research.swtch.com/2009/11/go-data-structures.html

正如其他评论者所指出的,你需要看一下Go函数的值语义 - 它们可能会有一些令人惊讶的地方。

如果你有以下函数:

func (t MyType) myFunc() {
// do something to set a field in t
}

并且在你的代码中调用

myVar.myFunc()

你可能会惊讶地发现这并不是你想要的,因为在myFunc()中看到的t实际上是myVar的一个_副本_。

但是,以下代码将会起作用:

func (t *myType) myFunc() {
// do something to set a field in t
}

因为该函数有一个_指针_的副本,并且可以通过该指针访问底层结构。

英文:

Don't worry -- Go will let you shoot yourself in the foot if you really want to 哪些类型在Google Go语言中是可变的和不可变的?

Go is not like Erlang, which might be what you are getting at with the question.

x := 1
x = 2

allocates one variable, x, with a value of 1, then reassigns it to 2 -- no additional memory is allocated here.

As you note, strings are immutable, so doing a string manipulation can result in making copies. If you find that you want to do in-place modifications to character data, you'll probably want to operate on variables of []byte via the bytes package.

Russ Cox's post about this should answer most of your questions about basic data structures: <http://research.swtch.com/2009/11/go-data-structures.html>

As other commenters noted, you'll want to look at the value semantics of Go functions -- they might be a little surprising at first.

If you have the following function:

func (t MyType) myFunc() {
    // do something to set a field in t
}

and you call in your code

myVar.myFunc()

you might be surprised to see that this doesn't do what you want because the t that is seen in myFunc() is really a copy of myVar.

But, the following will work:

func (t *myType) myFunc() {
    // do something to set a field in t
}

because the function has a copy of the pointer and can access the underlying structure via that pointer.

答案2

得分: 17

在我看来,首先应该区分以下两个概念:

  • 整数作为数学对象(即:值)

  • 类型为<code>int</code>的变量

然后答案是:整数变量是可变的,整数值是不可变的。

这个观点与Go规范一致,规范指出字符串是不可变的。显然,字符串变量是可变的。

Go中的变量(作为一个概念)至少有:

  • 命名变量(例如:<code>var i int</code>)
  • 可通过指针访问的变量

可变的Go对象:

  • 数组和切片
  • 映射
  • 通道
  • 至少从外部作用域捕获了一个变量的闭包

不可变的Go对象:

  • 接口
  • 布尔值、数值(包括类型为<code>int</code>的值)
  • 字符串
  • 指针
  • 函数指针,以及可以简化为函数指针的闭包
  • 只有一个字段的结构体

一些人可能认为可变的Go对象,而其他人可能认为它们是不可变的:

  • 有多个字段的结构体
英文:

In my opinion, one should first separate the following two concepts:

  • integers as mathematical objects (that is: values)

  • variables of type <code>int</code>

Then the answer is: Integer variables are mutable, integer values are immutable.

This view is consistent with the Go specification which states that strings are immutable. Obviously, a string variable is mutable.

Variables (as a concept) in Go are at least:

  • named variables (such as: <code>var i int</code>)
  • variables accessible via pointers

Mutable Go objects:

  • arrays and slices
  • maps
  • channels
  • closures which are capturing at least 1 variable from the outer scope

Immutable Go objects:

  • interfaces
  • booleans, numeric values (including values of type <code>int</code>)
  • strings
  • pointers
  • function pointers, and closures which can be reduced to function pointers
  • structs having a single field

Go objects which some people may consider mutable, while other people may consider them immutable:

  • structs having multiple fields

答案3

得分: 3

"可变性"只在谈论某些复合类型时才有意义,即具有"内部"部分的类型,这些部分可能可以独立于包含它的对象进行更改。字符串自然由字符组成,在语言中没有机制可以更改现有字符串中的字符,除非分配一个全新的字符串,因此我们说它是不可变的。

对于整数而言,谈论可变性并没有太多意义,因为整数有什么"组成部分"呢?通过分配一个全新的整数来更改整数,但是赋值并不算作"变异"。

可变性和引用类型与值类型之间存在一些联系。从语义上讲,不可变的引用类型和值类型之间没有区别。为什么?假设整数实际上是一个指向不可变对象的指针(即没有用于更改InternalIntObject的函数的*InternalIntObject)。一旦将这样的指针分配给一个变量,它将永远表示相同的整数值(不能被共享相同对象的其他人更改),因为对象是不可变的。这与整数值类型的行为相同。您可以通过赋值运算符分配整数;同样,您可以通过赋值分配这些指针;结果将是相同的:分配的变量表示与其分配的整数相同。唯一的区别是比较和算术运算符必须重新定义以解引用指针来计算结果。

因此,可变性只对引用类型有意义。

至于您所问的,"可变"类型通常被认为是引用类型,除了字符串之外:映射、通道、切片(与切片指向的数据相关),以及指向任何内容的指针(因为您可以更改指针指向的位置的值)。

英文:

"Mutability" only makes sense when you talk about some composite type, something that has "internal" parts, that perhaps can be changed independently of the thing that contains it. Strings are naturally composed of characters, and there is no mechanism in the language that lets us change a character in an existing string, short of assigning a whole new string, so we say that it is immutable.

For an int, it doesn't really make sense to talk about mutability, because, what are the "components" of an int? You change an int by assigning a whole new int, but assignment does not count as "mutating".

There is some connection between the issues of mutability and reference vs. value types. Semantically, there is no difference between an immutable reference type and a value type. Why? Suppose int was actually a pointer to an immutable object (i.e. *InternalIntObject with no functions for changing InternalIntObject). Once you assign such a pointer to a variable, it will forever represent the same integer value (can't be changed by others who share the same object) since the object is immutable. This is the same behavior as an integer value type. You can assign ints by assignment operator; likewise you can assign these pointers by assignment; the result would be the same: the assigned variable represents the same integer as what it was assigned to. Only difference would be the comparison and arithmetic operators would have to be re-defined to de-reference the pointer to compute the result.

Mutability is therefore only meaningful for reference types.

As for what you asked, the "mutable" types are generally considered to be the reference types except string: maps, channels, slices (with respect to the data pointed to by the slice), and also pointers to anything (since you can mutate the value at the location pointed to by the pointer).

答案4

得分: 2

是的,在Go规范中,单词_immutable_只出现了一次。而且那是在讨论type string时。我认为你应该从可赋值性可寻址性的双重视角来看待它。例如,Go会禁止你将变量重新绑定到具有未导出属性的不同类型的值,显然是不允许的。有点像C++中对于不提供复制构造函数的类,但在Go中,Pimpl感觉不那么尴尬,更适合goroutines的**通过通信共享**的哲学。

英文:

Yes, the word immutable comes up exactly once in Go spec. And that's when discussing type string. I think you should look at it more from the dual view points of Assignability and Addressability. For example Go will forbid you from rebinding variable to a different value of a type with unexported properties, obviously. Kinda like in C++ for classes not providing copy constructor, but in Go Pimpl feels a lot less awkward, befitting the goroutines' share by communicating philosophy.

答案5

得分: 1

你的关注似乎更多关于分配而不是不可变性。不可变性通过使内存重用成为可能来影响分配。一个聪明的编译器可以重新使用任何它知道地址不会逃逸的“不可变”内存。

除了字符串之外,对接口要小心。任何大于字大小的东西在分配给接口时都必须进行分配(除了优化之外)。此外,在循环体中声明的变量,如果其地址逃逸,包括通过闭包,每次循环都必须进行分配。否则,赋值只是一个赋值。该值只是被复制到变量所表示的内存中。

如果在循环中使用make或new,或者任何产生引用的文字,将必须进行分配(同样,受优化的影响)。

基本上,这一切归结为尽可能地重用内存,并希望编译器在你无法做到时为你做到,如果这样做有意义的话。

英文:

Your concern seems to be more about allocation than immutability. Immutability certainly impacts allocation by making it possible to reuse memory. A clever compiler could conceivably reuse any "immutable" memory whose address it knows doesn't escape.

Aside from strings, be careful with interfaces. Anything larger than word size will have to be allocated when assigned to the interface (optimizations aside). Also, variables declared in a loop body whose addresses escape, including via a closure, will have to be allocated each time through the loop. Otherwise, though, an assignment is just an assignment. The value just gets copied into the memory represented by the variable.

If you use make or new in a loop, or any literal that produces a reference, allocation will have to happen (again, subject to optimization).

Basically, it all boils down to trying to reuse memory where you can, and hoping the compiler does it for you when you can't, if it makes any sense to do so.

huangapple
  • 本文由 发表于 2011年11月5日 11:54:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/8018081.html
匿名

发表评论

匿名网友

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

确定