除了临时将方法分配给变量之外,是否有其他方法将方法绑定到Go地图上?

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

Is there any way to bind methods to a Go map other than temporarily assigning them to a variable?

问题

这段代码运行良好,但用于调用函数的临时变量感觉有些笨拙。

package main

import "fmt"

type Foo struct {
    name  string
    value int
}

// SetName接收一个Foo的指针,以便修改它。
func (f *Foo) SetName(name string) {
    f.name = name
}

var users = map[string]Foo{}

func main() {
    // 注意Foo{}。new(Foo)只是&Foo{}的语法糖,
    // 我们不需要一个指向Foo的指针,所以我进行了替换。
    // 尽管与问题无关。
    //p := Foo{}
    users["a"] = Foo{value: 1}
    x := users["a"]
    x.SetName("Abc")
    users["a"] = x
    fmt.Println(users)
}

点击此处查看代码

英文:

this code works fine but the temp var used to call the function feels clunky

package main

import "fmt"

type Foo struct {
	name  string
	value int
}

// SetName receives a pointer to Foo so it can modify it.
func (f *Foo) SetName(name string) {
	f.name = name
}

var users = map[string]Foo{}

func main() {
	// Notice the Foo{}. The new(Foo) was just a syntactic sugar for &Foo{}
	// and we don't need a pointer to the Foo, so I replaced it.
	// Not relevant to the problem, though.
	//p := Foo{}
	users["a"] = Foo{value: 1}
	x := users["a"]
	x.SetName("Abc")
	users["a"] = x
	fmt.Println(users)
}

http://play.golang.org/p/vAXthNBfdP

答案1

得分: 4

很遗憾,Go语言中通常情况下指针是透明的,当你在值上调用指针方法时,值会自动被取地址。但你找到了其中一种例外情况,即map存储。在map中的值被认为是不可寻址的,也就是说,你永远不能这样做val := &map[key]

当你有一个值val := Typ{}并且在*Typ上定义了方法时,当你尝试调用val.Method()时,Go语言会秘密地执行(&val).Method()。由于你不能对&map[key]这样做,所以这种方式不起作用,你所做的临时变量操作是唯一的方法。

至于为什么会这样,map的内部对用户来说是一种秘密,因为它是一个哈希表,它保留了重新分配自身、重新排列数据等操作的权利,允许你取任何值的地址会削弱这一点。已经有一些提案考虑允许这种特定情况下的操作(即:在指针接收器上调用方法),因为修复方法非常简单,但目前还没有被接受。也许将来会允许,但现在还不行。

英文:

Unfortunately no. In Go typically pointers are transparent, and values get auto-addressed when you call pointer methods on them. You managed to find one of the few cases where they aren't. That case is map storage -- values in maps are not considered addressable. That is, you can never do val := &map[key].

When you have a value val := Typ{} and methods defined on *Typ, when you try to call val.Method() Go will super secretly do (&val).Method(). Since you can't do &map[key], then this doesn't work so that temporary variable dance you do is the only way.

As for why that's the case, the internals of a map are considered a bit secret to the user, since it's a hashmap it reserves the right to reallocate itself, shuffle around data, etc, allowing you to take the address of any value undermines that. There have been proposals considered to allow this specific case to work (that is: calling a method with a pointer receiver on it), since the fix is so easy, but none have been accepted yet. It may be allowed someday, but not right now.

答案2

得分: 1

根据Jsor的详细解释:如果你确实需要调用映射值的方法,目前似乎唯一的方法是使用值的指针。

var users = make(map[string]*Foo)

func main() {
    users["a"] = &Foo{value: 1}
    users["a"].SetName("Abc")
    fmt.Println(users["a"])
}

但是这样做会失去有意义地打印它们的能力(现在的值只是内存地址)。你需要为*Foo编写一个自定义的打印函数:

func (f *Foo) String() string {
    return fmt.Sprintf("%v", *f)
}

http://play.golang.org/p/6-y2ewdnre

英文:

Following Jsor’s detailed explanation: if you really need to call methods of map values, it seems the only way for now is to use pointers for values.

var users = make(map[string]*Foo)

func main() {
    users["a"] = &Foo{value: 1}
    users["a"].SetName("Abc")
    fmt.Println(users["a"])
}

But that loses you, precisely, the ability to meaningfully print them (values are just memory addresses now). You’d need to write a custom printing function for *Foo:

func (f *Foo) String() string {
    return fmt.Sprintf("%v", *f)
}

http://play.golang.org/p/6-y2ewdnre

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

发表评论

匿名网友

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

确定