Os mapas são passados ​​por valor ou por referência no Go?

94

Os mapas são passados ​​por valor ou referência no Go?

Sempre é possível definir uma função como a seguir, mas isso é um exagero?

func foo(dat *map[string]interface{}) {...}

Mesma pergunta para o valor de retorno. Devo retornar um ponteiro para o mapa ou retornar o mapa como valor?

A intenção é evitar a cópia desnecessária de dados.

chmike
fonte
4
blog.golang.org/go-maps-in-action : os tipos de mapa são tipos de referência, como ponteiros ou fatias e, portanto, o valor de m acima é nulo; não aponta para um mapa inicializado. Um mapa nil se comporta como um mapa vazio durante a leitura, mas tentativas de gravar em um mapa nil causarão um pânico no tempo de execução; não faça isso. Para inicializar um mapa, use a função integrada de criação
mh-cbon
2
Tudo no Go é passado por valor. Alguns valores são ponteiros ou estruturas que contêm ponteiros. (você pode querer um *mapem alguns casos, se precisar reatribuir o valor do mapa em um endereço)
JimB
mh-cbon, não há tipos de referência no Go.
Inanc Gumus
@ mh-cbon Eu não estava falando sobre um tipo de referência. Eu estava perguntando se um mapa é passado por referência, o que equivale a perguntar se o endereço do mapa é passado como argumento ou uma "cópia" do mapa (passado por valor).
chmike de
1
@ mh-cbon Exatamente, mapas são ponteiros para hmap.
Inanc Gumus

Respostas:

83

Neste tópico você encontrará sua resposta:

Golang: Acessando um mapa usando sua referência

Você não precisa usar um ponteiro com um mapa.

Os tipos de mapa são tipos de referência, como ponteiros ou fatias [1]

Se você precisar alterar a Sessão, pode usar um ponteiro:

map[string]*Session

https://blog.golang.org/go-maps-in-action

Boris Le Méec
fonte
16
Para evitar pitfals, esteja ciente de que os mapas são passados ​​por referência apenas uma vez inicializados e, quando reinicializados dentro de uma função, a referência original não será atualizada. Aqui está um exemplo ilustrativo de playground: play.golang.org/p/Q6vrAmmJWR6 Ou um artigo completo de Dave Cheny dave.cheney.net/2017/04/29/there-is-no-pass-by-reference-in-go
Sindre Myren
19

Aqui estão algumas partes de Se um mapa não é uma variável de referência, o que é? por Dave Cheney:

Um valor de mapa é um ponteiro para uma runtime.hmapestrutura.

e conclusão:

Conclusão

Mapas, como canais, mas ao contrário de fatias, são apenas ponteiros para tipos de tempo de execução. Como você viu acima, um mapa é apenas um indicador para uma runtime.hmap estrutura.

Os mapas têm a mesma semântica de ponteiro que qualquer outro valor de ponteiro em um programa Go. Não há mágica, exceto a reescrita da sintaxe do mapa pelo compilador em chamadas para funções em runtime/hmap.go.

E um pouco interessante sobre a história / explicação da mapsintaxe:

Se os mapas são indicadores, não deveriam ser *map[key]value?

É uma boa pergunta que se os mapas são valores de ponteiro, por que a expressão make(map[int]int)retorna um valor com o tipo map[int]int. Não deveria retornar um *map[int]int? Ian Taylor respondeu isso recentemente em um tópico 1 do Golang-nuts .

Nos primeiros dias, o que chamamos de mapas agora eram escritos como ponteiros, então você escreveu *map[int]int. Afastamo-nos disso quando percebemos que ninguém jamais escrevia mapsem escrever *map.

Indiscutivelmente, renomear o tipo de *map[int]intpara map[int]int, embora confuso porque o tipo não se parece com um ponteiro, era menos confuso do que um valor em forma de ponteiro que não pode ser desreferenciado.

Akavall
fonte
3

Não. Os mapas são referência por padrão.

    package main

    import "fmt"

    func mapToAnotherFunction(m map[string]int) {
        m["hello"] = 3
        m["world"] = 4
        m["new_word"] = 5
    }

    // func mapToAnotherFunctionAsRef(m *map[string]int) {
    // m["hello"] = 30
    // m["world"] = 40
    // m["2ndFunction"] = 5
    // }

    func main() {
        m := make(map[string]int)
        m["hello"] = 1
        m["world"] = 2

        // Initial State
        for key, val := range m {
            fmt.Println(key, "=>", val)
        }

        fmt.Println("-----------------------")

        mapToAnotherFunction(m)
        // After Passing to the function as a pointer
        for key, val := range m {
            fmt.Println(key, "=>", val)
        }

        // Try Un Commenting This Line
        fmt.Println("-----------------------")

        // mapToAnotherFunctionAsRef(&m)
        // // After Passing to the function as a pointer
        // for key, val := range m {
        //  fmt.Println(key, "=>", val)
        // }

        // Outputs
        // hello => 1
        // world => 2
        // -----------------------
        // hello => 3
        // world => 4
        // new_word => 5
        // -----------------------

    }

Do Golang Blog-

Os tipos de mapa são tipos de referência, como ponteiros ou fatias e, portanto, o valor de m acima é nulo; não aponta para um mapa inicializado. Um mapa nil se comporta como um mapa vazio durante a leitura, mas tentativas de gravar em um mapa nil causarão um pânico no tempo de execução; não faça isso. Para inicializar um mapa, use a função integrada make:

// Ex of make function
m = make(map[string]int)

Link do snippet de código Brinque com ele.

Alamin
fonte