Esta semana, eu estava trabalhando em um dos pacotes wrapper da API para golang, que tratava do envio de solicitações de postagem com valores codificados em URL, configuração de cookies e todas as coisas divertidas. No entanto, enquanto construía o corpo, usei o tipo url.Value para construir o corpo e usei-o para adicionar e definir pares de valores-chave. No entanto, eu estava recebendo um erro de referência de ponteiro nulo com fio em algumas partes, pensei que fosse por causa de algumas das variáveis que defini manualmente. No entanto, ao depurar mais de perto, descobri uma armadilha comum ou má prática de apenas declarar um tipo, mas inicializá-lo e causar erros de referência nulos.
Neste post, irei abordar o que são mapas, como criá-los e, principalmente, como declará-los e inicializá-los adequadamente. Crie uma distinção adequada entre a declaração e inicialização de mapas ou qualquer tipo de dados semelhante em golang.O que é um mapa em Golang?
Como criar mapas em Golang
package main import "fmt" func main() { words := "hello how are you" letters := map[string]int{} for _, word := range words { wordCount[word] } fmt.Println("Word counts:") for word, count := range wordCount { fmt.Printf("%s: %d\n", word, count) } }$ vá executar main.go Contagem de palavras: e: 2 : 3 w: 1 R: 1 você: 1 você: 1 h: 2 eu: 2 ó: 3 uma: 1
$ go run main.go Word counts: e: 2 : 3 w: 1 r: 1 y: 1 u: 1 h: 2 l: 2 o: 3 a: 1Portanto, ao inicializar o mapa como map[string]int{} você obterá um mapa vazio. Isso pode ser usado para preencher as chaves e valores, iteramos sobre a string e, para cada caractere (runa), lançamos esse byte de caractere na string e incrementamos o valor, o valor zero para int é 0, então, por padrão se a chave não estiver presente, será zero, mas é uma faca de dois gumes, nunca sabemos que a chave está presente no mapa com o valor 0 ou a chave não está presente, mas o valor padrão é 0. Para isso, é necessário verificar se a chave existe ou não no mapa.
Para mais detalhes, você pode conferir minha postagem do Golang Maps em detalhes.
Diferença entre declaração e inicialização
Portanto, a declaração simplesmente disponibiliza a variável dentro do escopo do programa. Para mapas e fatias, declarar uma variável sem inicialização define-a como nula, o que significa que ela não aponta para nenhuma memória alocada e não pode ser usada diretamente.
Enquanto a inicialização aloca memória e define a variável para um estado utilizável. Para mapas e fatias, você precisa inicializá-los explicitamente usando sintaxe como myMap = make(map[keyType]valueType) ou slice = []type{}. Sem essa inicialização, a tentativa de usar o mapa ou fatia levará a erros de tempo de execução, como pânico ao acessar ou modificar um mapa ou fatia nula.
Vejamos os valores de um mapa quando ele é declarado/inicializado/não inicializado.
Imagine que você está construindo um gerenciador de configuração que lê as configurações de um mapa. O mapa será declarado globalmente, mas inicializado somente quando a configuração for carregada.
package main import "fmt" func main() { words := "hello how are you" letters := map[string]int{} for _, word := range words { wordCount[word] } fmt.Println("Word counts:") for word, count := range wordCount { fmt.Printf("%s: %d\n", word, count) } }$ vá executar main.go Porta do servidor: configuração não encontrada
$ go run main.go Word counts: e: 2 : 3 w: 1 r: 1 y: 1 u: 1 h: 2 l: 2 o: 3 a: 1
package main import "fmt" func main() { words := "hello how are you" letters := map[string]int{} for _, word := range words { wordCount[word] } fmt.Println("Word counts:") for word, count := range wordCount { fmt.Printf("%s: %d\n", word, count) } }$ vá executar main.go Porta do servidor: 8080
$ go run main.go Word counts: e: 2 : 3 w: 1 r: 1 y: 1 u: 1 h: 2 l: 2 o: 3 a: 1
package main import "fmt" func main() { words := "hello how are you" letters := map[string]int{} for _, word := range words { wordCount[word] } fmt.Println("Word counts:") for word, count := range wordCount { fmt.Printf("%s: %d\n", word, count) } }$ vá executar main.go Definições de configuração inicializadas Porta do servidor: 8080
$ go run main.go Word counts: e: 2 : 3 w: 1 r: 1 y: 1 u: 1 h: 2 l: 2 o: 3 a: 1No código acima, declaramos o mapa global configSettings, mas não o inicializamos naquele ponto, até que quiséssemos acessar o mapa. Inicializamos o mapa na função principal, esta função principal poderia ser outras partes específicas do código, e a variável global configSettings um mapa de outra parte do código, ao inicializá-la no escopo necessário, evitamos que ela cause ponteiro nulo erros de acesso. Só inicializamos o mapa se ele for nulo, ou seja, não foi inicializado em nenhum outro lugar do código. Isso evita substituir o mapa/eliminar o conjunto de configurações de outras partes do escopo.
Armadilhas no acesso a mapas não inicializados
Vamos dar uma olhada em um exemplo, um caso real onde isso pode acontecer.
$ go run main.go Word counts: e: 2 : 3 w: 1 r: 1 y: 1 u: 1 h: 2 l: 2 o: 3 a: 1Isso resultará em pânico no tempo de execução.
$ go run main.go Word counts: e: 2 : 3 w: 1 r: 1 y: 1 u: 1 h: 2 l: 2 o: 3 a: 1Isso ocorre porque url.Values é um mapa de string e uma lista de valores de string. Como o tipo subjacente é um mapa para Valores, e no exemplo, apenas declaramos a variável vals com o tipo url.Values, ela apontará para uma referência nula, daí a mensagem ao adicionar o valor ao tipo. Portanto, é uma boa prática usar make ao declarar ou inicializar um tipo de dados de mapa. Se você não tiver certeza de que o tipo subjacente é map, poderá usar Type{} para inicializar um valor vazio desse tipo.
package main import "fmt" func main() { words := "hello how are you" letters := map[string]int{} for _, word := range words { wordCount[word] } fmt.Println("Word counts:") for word, count := range wordCount { fmt.Printf("%s: %d\n", word, count) } }$ vá executar urlvals.go mapa[foo:[barra]] foo=barra
$ go run main.go Word counts: e: 2 : 3 w: 1 r: 1 y: 1 u: 1 h: 2 l: 2 o: 3 a: 1Também é recomendado pela equipe golang usar a função make ao inicializar um mapa. Portanto, use make para mapas, fatias e canais ou inicialize a variável de valor vazia com Type{}. Ambos funcionam de forma semelhante, mas o último também é aplicável de forma mais geral a estruturas.
Conclusão
Seguindo as práticas recomendadas, como usar a função make ou inicializar com Type{}, você pode evitar problemas comuns relacionados a mapas não inicializados. Sempre certifique-se de que os mapas e fatias sejam explicitamente inicializados antes do uso para proteger contra desreferências inesperadas de ponteiro nulo
Obrigado por ler esta postagem. Se você tiver dúvidas, comentários e sugestões, sinta-se à vontade para deixá-los nos comentários.
Boa codificação :)
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3