Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dereferencing a map index in Golang

Tags:

pointers

go

I'm learning Go currently and I made this simple and crude inventory program just to tinker with structs and methods to understand how they work. In the driver file I try to call a method from and item type from the items map of the Cashier type. My method have pointer reciever to use the structs directly instead of making copies. When I run the program I get this error .\driver.go:11: cannot call pointer method on f[0] .\driver.go:11: cannot take the address of f[0]

Inventory.go:

package inventory


type item struct{
    itemName string
    amount int
}

type Cashier struct{
    items map[int]item
    cash int
}

func (c *Cashier) Buy(itemNum int){
    item, pass := c.items[itemNum]

    if pass{
        if item.amount == 1{
            delete(c.items, itemNum)
        } else{
            item.amount--
            c.items[itemNum] = item 
        }
        c.cash++
    }
}


func (c *Cashier) AddItem(name string, amount int){
    if c.items == nil{
        c.items = make(map[int]item)
    }
    temp := item{name, amount}
    index := len(c.items)
    c.items[index] = temp
}

func (c *Cashier) GetItems() map[int]item{
    return c.items;
}

func (i *item) GetName() string{
    return i.itemName
}

func (i *item) GetAmount() int{
    return i.amount
}

Driver.go:

package main

import "fmt"
import "inventory"

func main() {
    x := inventory.Cashier{}
    x.AddItem("item1", 13)
    f := x.GetItems()

    fmt.Println(f[0].GetAmount())
}

The part of the code that really pertains to my problem is the GetAmount function in inventory.go and print statement in the driver.go

like image 592
user1721803 Avatar asked Nov 26 '13 17:11

user1721803


3 Answers

A map entry cannot be addressed (as its address might change during map growth/shrink), so you cannot call pointer receiver methods on them.

Detail here: https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/4_pabWnsMp0

like image 196
Volker Avatar answered Oct 11 '22 03:10

Volker


As Volker said in his answer - you can't get address of an item in the map. What you should do - is to store pointers to items in your map, instead of storing item values:

package main

import "fmt"

type item struct {
    itemName string
    amount   int
}

type Cashier struct {
    items map[int]*item
    cash  int
}

func (c *Cashier) Buy(itemNum int) {
    item, pass := c.items[itemNum]

    if pass {
        if item.amount == 1 {
            delete(c.items, itemNum)
        } else {
            item.amount--
        }
        c.cash++
    }
}

func (c *Cashier) AddItem(name string, amount int) {
    if c.items == nil {
        c.items = make(map[int]*item)
    }
    temp := &item{name, amount}
    index := len(c.items)
    c.items[index] = temp
}

func (c *Cashier) GetItems() map[int]*item {
    return c.items
}

func (i *item) GetName() string {
    return i.itemName
}

func (i *item) GetAmount() int {
    return i.amount
}

func main() {
    x := Cashier{}
    x.AddItem("item1", 13)
    f := x.GetItems()
    fmt.Println(f[0].GetAmount()) // 13
    x.Buy(0)
    f = x.GetItems()
    fmt.Println(f[0].GetAmount()) // 12
}

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

like image 22
Kluyg Avatar answered Oct 11 '22 05:10

Kluyg


While the other answers are useful, I think in this case it is best just to make non-mutating functions not take a pointer:

func (i item) GetName() string{
    return i.itemName
}

func (i item) GetAmount() int{
    return i.amount
}
like image 3
Andrew W. Phillips Avatar answered Oct 11 '22 04:10

Andrew W. Phillips