web-dev-qa-db-fra.com

Go: opération non valide - la valeur * map [key] ne prend pas en charge l'indexation

J'essaie d'écrire une fonction qui modifie la carte d'origine qui est passée par un pointeur mais Go ne le permet pas. Disons que j'ai une grande carte et que je ne veux pas la copier d'avant en arrière.

Le code qui utilise le passage par valeur fonctionne et fait ce dont j'ai besoin mais implique de passer par valeur ( terrain de je ):

package main

import "fmt"

type Currency string

type Amount struct {
    Currency Currency
    Value float32
}

type Balance map[Currency]float32

func (b Balance) Add(amount Amount) Balance {
    current, ok := b[amount.Currency]
    if ok {
        b[amount.Currency] = current + amount.Value
    } else {
        b[amount.Currency] = amount.Value
    }
    return b
}

func main() {
    b := Balance{Currency("USD"): 100.0}
    b = b.Add(Amount{Currency: Currency("USD"), Value: 5.0})

    fmt.Println("Balance: ", b)
}

Mais si j'essaie de passer le paramètre comme pointeur comme ici ( terrain de je ):

func (b *Balance) Add(amount Amount) *Balance {
    current, ok := b[amount.Currency]
    if ok {
        b[amount.Currency] = current + amount.Value
    } else {
        b[amount.Currency] = amount.Value
    }
    return b
}

Je reçois une erreur de compilation:

prog.go:15: invalid operation: b[amount.Currency] (type *Balance does not support indexing)
prog.go:17: invalid operation: b[amount.Currency] (type *Balance does not support indexing)
prog.go:19: invalid operation: b[amount.Currency] (type *Balance does not support indexing)

Comment dois-je gérer cela?

18

Vous essayez d'indexer sur le pointeur plutôt que sur la carte elle-même. Un peu déroutant car généralement avec des pointeurs vs des valeurs, le déréférencement est automatique pour les structures. Si votre structure n'est qu'une carte, cependant, elle n'est transmise que par référence de sorte que vous n'avez pas à vous soucier de créer des méthodes qui agissent sur des pointeurs pour éviter de copier la structure entière à chaque fois. Le code suivant est équivalent à votre premier extrait mais utilise un type de pointeur.

package main

import "fmt"

type Currency string

type Amount struct {
    Currency Currency
    Value float32
}

type Balance map[Currency]float32

func (b *Balance) Add(amount Amount) *Balance {
    current, ok := (*b)[amount.Currency]
    if ok {
        (*b)[amount.Currency] = current + amount.Value
    } else {
        (*b)[amount.Currency] = amount.Value
    }
    return b
}

func main() {
    b := &Balance{Currency("USD"): 100.0}
    b = b.Add(Amount{Currency: Currency("USD"), Value: 5.0})

    fmt.Println("Balance: ", (*b))
}

Mais pour savoir comment y faire face: si votre structure est juste de type map, je ne m'inquiéterais pas d'écrire vos fonctions de réception pour prendre des pointeurs, et juste recevoir la valeur puisque la valeur n'est de toute façon qu'une référence. Faites comme dans votre extrait d'origine.

30
Serdmanczyk

Vous pouvez simplement déréférencer b: (*b)

https://play.golang.org/p/Xq6qFy4_PC

func (b *Balance) Add(amount Amount) *Balance {
    current, ok := (*b)[amount.Currency]
    if ok {
        (*b)[amount.Currency] = current + amount.Value
    } else {
        (*b)[amount.Currency] = amount.Value
    }
    return b
}

Mise à jour

@Serdmanczyk fait un bon point ... vous pouvez passer une carte en toute sécurité par valeur, la carte sous-jacente sera mise à jour, pas une copie de la carte. C'est-à-dire; passe-par-valeur dans le cas d'une carte signifie passer l'adresse de la carte, pas le contenu de la carte.

Voir https://play.golang.org/p/i7Yz4zMq4v

type foo map[string]string

func main() {
    a := foo{}
    a["hello"] = "world"
    fmt.Printf("%#v\n", a)
    mod(a)
    fmt.Printf("%#v\n", a)

}

func mod(f foo) {
    f["hello"] = "cruel world"
}

Quelles sorties:

main.foo{"hello":"world"}
main.foo{"hello":"cruel world"}
11
John Weldon