地图接口指针方法接收器

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

map interface pointer method receiver

问题

我有以下代码:

package main

import "fmt"

type Variables struct {
	sum     uint64
	highest uint64
}

type Data struct {
	count  uint64
	mValue map[string]Variables
}

func (v Variables) Add(value Variables) Variables {
	v.sum += value.sum
	if v.highest == 0 {
		v.highest = value.highest
	} else if v.highest < value.highest {
		v.highest = value.highest
	}
	return v
}

func (v *Variables) AddPointer(value Variables) {
	v.sum += value.sum
	if v.highest == 0 {
		v.highest = value.highest
	} else if v.highest < value.highest {
		v.highest = value.highest
	}
}

func main() {
	var instances [2]Variables
	instances[0] = Variables{sum: 5, highest: 3}
	instances[1] = Variables{sum: 10, highest: 2}
	var d Data
	d.mValue = make(map[string]Variables)
	for i := 0; i < len(instances); i++ {
		d.mValue["one"] = d.mValue["one"].Add(instances[i])
		d.mValue["two"].AddPointer(instances[i])
	}
	fmt.Println(d.mValue["one"])
	fmt.Println(d.mValue["two"])
}

我得到了以下错误:

# command-line-arguments
/tmp/sandbox209565070/main.go:42: cannot call pointer method on d.mValue["two"]
/tmp/sandbox209565070/main.go:42: cannot take the address of d.mValue["two"]

(我认为)我理解第二个错误cannot take address - 因为它是一个映射,不能取地址(这样理解对吗?)

第一个错误也是同样的原因吗(不能调用指针方法)?

是否有办法在映射中使用指针方法来操作结构体?

英文:

I have a the following code

http://play.golang.org/p/d-bZxL72az

package main
import &quot;fmt&quot;
type Variables struct {
sum     uint64
highest uint64
}
type Data struct {
count  uint64
mValue map[string]Variables
}
func (v Variables) Add(value Variables) Variables {
v.sum += value.sum
if v.highest == 0 {
v.highest = value.highest
} else if v.highest &lt; value.highest {
v.highest = value.highest
}
return v
}
func (v *Variables) AddPointer(value Variables) {
v.sum += value.sum
if v.highest == 0 {
v.highest = value.highest
} else if v.highest &lt; value.highest {
v.highest = value.highest
}
}
func main() {
var instances [2]Variables
instances[0] = Variables{sum: 5, highest: 3}
instances[1] = Variables{sum: 10, highest: 2}
var d Data
d.mValue = make(map[string]Variables)
for i:= 0; i &lt; len(instances); i++ {
d.mValue[&quot;one&quot;] = d.mValue[&quot;one&quot;].Add(instances[i])
d.mValue[&quot;two&quot;].AddPointer(instances[i])
}
fmt.Println(d.mValue[&quot;one&quot;])
fmt.Println(d.mValue[&quot;two&quot;])
}

I get the error

# command-line-arguments
/tmp/sandbox209565070/main.go:42: cannot call pointer method on d.mValue[&quot;two&quot;]
/tmp/sandbox209565070/main.go:42: cannot take the address of d.mValue[&quot;two&quot;]

(I think) I understand the second error cannot take address - because, it is a map, it cannot take the address (is that correct?)

Is it the same reason for the first error as well (cannot call pointer method)?

Is there a way to use pointer methods on structures that are within the maps..

答案1

得分: 3

是的,同样的原因。为了调用一个带有指针接收器的方法,你要么需要事先有一个指针,要么需要一个可寻址的值,Go 会自动为你取得指针。

那么,你可以将 mValue 定义为 map[string]*Variables,而不是 map[string]Variables。这样,你将在映射中存储一个指向已分配、保证可寻址的 Variables 的指针,然后你就可以在该指针上调用方法了。

英文:

Yes, same reason. In order to call a method with a pointer receiver, you either need to have a pointer in the first place, or you need an addressable value and Go will automatically take the pointer for you.

What you can do, then, is to make mValue a map[string]*Variables instead of a map[string]Variables. Then you will be storing a pointer to an already-allocated, guaranteed-addressable Variables in the map, and you'll be able to call methods on that pointer.

答案2

得分: 0

进一步扩展前面的答案...

实际上,这通常不是一个问题。如果类型在没有指针的情况下更有意义(例如,对于值语义更有意义的小结构体),那么您就不会使用指针接收器,因此不会出现这个问题。

如果指针接收器有意义,那么在大多数情况下,您应该在大多数地方使用指向该类型的指针,例如在映射中(正如hobbs所说),并且您不会有接受非指针参数或返回非指针值的方法(非指针接收器仍然可能有意义,并且很容易使用)。同样,不会出现这个问题。

在第一种情况下,如果您想在非指针映射条目中使用指针接收器,您可以使用临时(可寻址)变量并将其重新分配回映射。

x := d.mValue["two"]
x.AddPointer(instances[i])
// AddPointer使用指针接收器;`x`需要是可寻址的,
// 它将包含来自映射的值的副本,该副本可能会被方法更改,
// 因此我们需要将新版本的副本复制回映射中。
d.mValue["two"] = x

在第二种情况下,会出现一些问题。首先,为了避免nil指针,您需要初始化映射条目或在映射读取时检查nil/存在性(或使您的指针接收器方法处理nil值的接收器,但这对于非指针接收器方法没有帮助)。其次,如果由于某种愚蠢的原因您有指针但仍然有一个返回非指针的方法,您必须使用不同的语法来分配给映射。

可能是这样的

// 初始化一些映射条目以避免nil指针
d.mValue = map[string]*Variables{
    "one": &Variables{},
    "two": &Variables{},
}
for i := 0; i < len(instances); i++ {
    // 只调用非指针接收器很容易/没问题:
    d.mValue["one"].Add(instances[i])

    // 但是您不能这样做:
    //d.mValue["one"] = d.mValue["one"].Add(instances[i])
    // cannot use d.mValue["one"].Add(instances[i]) (type Variables) as type *Variables in assignment

    *d.mValue["one"] = d.mValue["one"].Add(instances[i])
    d.mValue["two"].AddPointer(instances[i])
}
英文:

To expand on the previous answer…

In practice, this isn't usually a problem. If the type makes more sense without pointers (e.g. a small struct where value semantics make more sense) then you wouldn't have pointer receivers and the issue wouldn't arise.

If pointer receivers make sense then you should probably be using pointers to the type in most places, such as in maps (as hobbs said) and you wouldn't have methods that took non-pointer arguments or returned non-pointer values (non-pointer receivers could still make sense and would be easy to use). Again, the issue wouldn't arise.

In the first case if you wanted to use a pointer receiver with a non-pointer map entry, you could use a temporary (addressable) variable and reassign it back into the map.

	x := d.mValue[&quot;two&quot;]
x.AddPointer(instances[i])
// AddPointer uses a pointer receiver; `x` needs to be addressable,
// it will contain a copy of the value from the map and that copy may
// be changed by the method so we need to copy the new version back
// into the map.
d.mValue[&quot;two&quot;] = x

In the second case a few issues arise. First, to avoid nil pointers you need to either initialize the map entries or check for nil/existance on map reads (or make make your pointer receiver methods handle nil valued receivers, but that doesn't help for non-pointer receiver methods though). Second, if for some silly reason you have pointers but still have a method that returned a non-pointer you'd have to use a different syntax to assign to the map.

Something like this perhaps:

    // Initialize some map entries to avoid nil pointers
d.mValue = map[string]*Variables{
&quot;one&quot;: &amp;Variables{},
&quot;two&quot;: &amp;Variables{},
}
for i := 0; i &lt; len(instances); i++ {
// Just calling the non-pointer reciever is easy/fine:
d.mValue[&quot;one&quot;].Add(instances[i])
// But you can&#39;t do this:
//d.mValue[&quot;one&quot;] = d.mValue[&quot;one&quot;].Add(instances[i])
// cannot use d.mValue[&quot;one&quot;].Add(instances[i]) (type Variables) as type *Variables in assignment
*d.mValue[&quot;one&quot;] = d.mValue[&quot;one&quot;].Add(instances[i])
d.mValue[&quot;two&quot;].AddPointer(instances[i])
}

huangapple
  • 本文由 发表于 2015年7月19日 03:17:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/31494710.html
匿名

发表评论

匿名网友

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

确定