简体   繁体   中英

map interface pointer method receiver

I have a the following code

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

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"])
}

I get the error

# 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"]

(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..

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.

To expand on the previous answer…

In practice, this isn't usually a problem. If the type makes more sense without pointers (eg 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["two"]
    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["two"] = 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{
        "one": &Variables{},
        "two": &Variables{},
    }
    for i := 0; i < len(instances); i++ {
        // Just calling the non-pointer reciever is easy/fine:
        d.mValue["one"].Add(instances[i])

        // But you can't do this:
        //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])
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM