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

发布于 2024-12-13 16:56:13 字数 489 浏览 2 评论 0原文

在 Google Go 中,我读到字符串是不可变的,好吧,但是是 int 吗? 那么其他类型呢?作为一个稍老的程序员,我更喜欢可变性,尽管我知道不变性的好处,但我更喜欢危险地生活。

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


更新,我最关心的是取决于类型可变或不可变的实际问题。正如 Java 中的典型示例一样,如果您在循环中创建一个 String 并循环 10,000 次,您将创建 10,000 个 String,然后这些 String 将被垃圾收集。这实际上是我工作过的公司的一个项目中的一个严重问题。

问题是,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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

三生殊途 2024-12-20 16:56:13

别担心——如果你真的想的话,Go 会让你搬起石头砸自己的脚 :-)

Go 不像 Erlang,这可能就是你问这个问题的目的。

x := 1
x = 2

分配一个变量 x,其值为 1,然后将其重新分配给 2——此处不分配额外的内存。

正如您所注意到的,字符串是不可变的,因此进行字符串操作可能会导致生成副本。如果您发现想要对字符数据进行就地修改,您可能需要通过 bytes 包对 []byte 变量进行操作。

Russ Cox 的相关文章应该可以回答您有关基本数据结构的大部分问题:http://research。 swtch.com/2009/11/go-data-structs.html

正如其他评论者指出的那样,您需要查看 Go 函数的值语义 - 一开始它们可能会有点令人惊讶。

如果您有以下函数:

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

并且您在代码中调用,

myVar.myFunc()

您可能会惊讶地发现这没有执行您想要的操作,因为在 myFunc()t code> 实际上是 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 :-)

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.

束缚m 2024-12-20 16:56:13

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

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

  • int 类型的变量

那么答案是:整数变量是可变的,整数值是不可变的。

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

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

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

可变 Go 对象:

  • 数组和切片
  • 映射
  • 通道
  • 闭包,捕获至少 1 个变量从外部范围来看

不可变的 Go 对象:

  • 接口
  • 布尔值、数值(包括 int 类型的值)
  • 字符串
  • 指针
  • 函数指针和闭包(可以简化为函数指针
  • 具有单个字段的结构体

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

  • 具有多个字段的结构

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

  • integers as mathematical objects (that is: values)

  • variables of type int

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: var i int)
  • 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 int)
  • 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
甜点 2024-12-20 16:56:13

“可变性”只有当您谈论某种复合类型时才有意义,这种复合类型具有“内部”部分,并且可能可以独立于包含它的事物而进行更改。字符串自然是由字符组成的,语言中没有任何机制可以让我们更改现有字符串中的字符,除非分配一个全新的字符串,所以我们说它是不可变的。

对于 int 来说,谈论可变性并没有什么意义,因为 int 的“组件”是什么?您可以通过分配一个全新的 int 来更改 int,但分配并不算作“变异”。

可变性和引用类型与值类型问题之间存在某种联系。从语义上讲,不可变引用类型和值类型之间没有区别。为什么?假设 int 实际上是一个指向不可变对象的指针(即 *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).

苍白女子 2024-12-20 16:56:13

是的,“不可变”这个词在 Go 规范中只出现过一次。这就是讨论类型字符串时的情况。我认为你应该更多地从 可分配性可寻址性。例如,显然,Go 将禁止您将变量重新绑定到具有未导出属性的类型的不同值。有点像 C++ 中不提供复制构造函数的类,但在 Go 中 Pimpl 感觉不那么尴尬了,符合 goroutine 的通过沟通来分享的理念。

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.

眼角的笑意。 2024-12-20 16:56:13

您的担忧似乎更多是关于分配而不是不变性。不变性肯定会通过使内存重用成为可能来影响分配。一个聪明的编译器可以想象重用任何它知道其地址不会转义的“不可变”内存。

除了字符串之外,还要注意接口。当分配给接口时,任何大于字大小的东西都必须被分配(除了优化)。此外,在循环体中声明的变量(其地址逃逸(包括通过闭包))每次都必须在循环中进行分配。但除此之外,任务就只是任务。该值只是被复制到变量表示的内存中。

如果您在循环中使用 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文