在Go语言中,如何在函数内重新分配外部引用?

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

In golang, how do I re-assign an external reference from within a function?

问题

我可能没有在问题中正确表达,但也许这段代码会更清楚一些:

package main
import "fmt"

type Blob struct {
    Message string
}

func assign1(bb **Blob) {
    *bb = &Blob{"Internally created by assign1"}
}

func (b *Blob) assign2() {
    *b = Blob{"Internally created by assign2"}
}

func main() {
    x1 := &Blob{"Hello World"}
    assign1(&x1)
    fmt.Printf("x1 = %+v\n", *x1)

    x2 := Blob{"Hello World"}
    x2.assign2()
    fmt.Printf("x2 = %+v\n", x2)
}

产生了期望的输出:

x1 = {Message:Internally created by assign1}
x2 = {Message:Internally created by assign2}

我想将一个引用(指向指针的指针)传递给一个函数,并让该函数为指针分配一个新值,以便调用范围可以看到该新值。

我已经找到了上述两种方法,但我想知道它们是否正确,或者是否存在某些隐藏的缺陷。另外,它们中的任何一种是否比另一种更符合惯用法?

从Java来的话,assign2 看起来有些不对,但我确定我在 encoding/json 包中见过类似的东西。那实际上是在做什么?

谢谢!

英文:

I'm probably not expressing this correctly in the question, but perhaps this code will make it clearer:

package main
import "fmt"

type Blob struct {
    Message string
}

func assign1(bb **Blob) {
    *bb = &Blob{"Internally created by assign1"}
}

func (b *Blob) assign2() {
    *b = Blob{"Internally created by assign2"}
}

func main() {
    x1 := &Blob{"Hello World"}
    assign1(&x1)
    fmt.Printf("x1 = %+v\n", *x1)

    x2 := Blob{"Hello World"}
    x2.assign2()
    fmt.Printf("x2 = %+v\n", x2)
}

Produces, as desired:

x1 = {Message:Internally created by assign1}
x2 = {Message:Internally created by assign2}

I want to pass a reference (pointer to a pointer) into a function and have the function assign a new value to the pointer such that the calling scope will see that new value.

I've figured out the above two ways of doing this, but I'd like to know if they are actually correct or if there is some hidden flaw. Also, are either of them more idiomatic than the other?

Coming from Java, assign2 just seems wrong but I'm sure I've seen something similar in the encoding/json package. What is that actually doing?

Thanks!

答案1

得分: 2

两种形式都是有效的Go语言,正如你所发现的。

对于assign2的情况,编译器发现assign2不在Blob的方法集中,但它是*Blob的方法集的一部分。然后,它将方法调用转换为:

(&x2).assign2()

尽管在你的示例中,如果一个方法然后去改变x2可能会令人困惑,但在标准库中有许多地方使用了这种模式。可能最常见的情况是使用encoding/json模块为类型实现自定义解码:你使用指针接收器实现DecodeJSON方法,并更新被指向的值。

英文:

Both forms are valid Go, as you've discovered.

For the assign2 case, the compiler finds that assign2 does not appear in Blob's method set, but it is part of *Blob's method set. It then converts the method call to:

(&x2).assign2()

While it can be confusing if a method then goes and changes x2 like in your example, there are a number of places in the standard library where this pattern is used. Probably the most common one is implementing custom decoding for a type with the encoding/json module: you implement the DecodeJSON method with a pointer receiver, and update the value being pointed to.

答案2

得分: 2

James回答assign2的机制,我稍微谈一下何时使用它。

首先,让我们看一个更简单的例子。

type Counter uint

func (c *Counter) Increment() {
    *c += 1
}

在计数器的例子中,接收器的整个状态都在发生变化。对于encoding/json包也是如此,接收器的整个状态都在发生变化。这是我会使用这种风格的唯一时机。

这种风格的一个主要优点是:你可以为这种变化定义一个接口,就像GobDecoder所做的那样。

当我第一次看到assign2风格时,我有点不太适应。但后来我想到(c *Counter) Increment在机器代码中被转换为Increment(c *Counter),这样就不再困扰我了。我个人更喜欢assign1风格。(尽管,如最初发布的代码所示,没有必要使用双指针。)

package main

import "fmt"

type Blob struct {
    Message string
}

func assign1(b *Blob) {
    *b = Blob{"Internally created by assign1"}
}

func main() {
    x1 := Blob{"Hello World"}
    assign1(&x1)
    fmt.Printf("x1 = %+v\n", *x1)
}
英文:

James answers the mechanics of assign2. I'll touch a bit on when to use it.

Let's take a simpler example, first.

type Counter uint

func (c *Counter) Increment() {
    *c += 1
}

In the counter example the entire state of the receiver is changing. Similarly for the encoding/json package the entire state of the receiver is changing. That's really the only time I would use that style.

One major advantage of the style: you can define an interface for the change, just like the GobDecoder does.

When I first saw the assign2 style it was a little grating. But then I remembered that (c *Counter) Increment gets translated to Increment(c *Counter) in the machine code and it didn't bother me anymore. I personally prefer assign1-style. (Though, there is no need for the double pointers as orignally posted.)

package main
import "fmt"

type Blob struct {
    Message string
}

func assign1(b *Blob) {
    *b = Blob{"Internally created by assign1"}
}

func main() {
    x1 := Blob{"Hello World"}
    assign1(&x1)
    fmt.Printf("x1 = %+v\n", *x1)
}

huangapple
  • 本文由 发表于 2015年3月3日 14:14:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/28825259.html
匿名

发表评论

匿名网友

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

确定