Aprendendo Go: New vs Make
A linguagem Go, é muito eficiente e simples, porém existem alguns conceitos que são chave para que mesmo com toda simplicidade se mantenha muito eficiente, hoje vamos falar sobre alocação de memória em Go.
Esse artigo foi baseado no documento, gratuito e disponível em Effective Go
Go possui, nativamente, dois diferente tipos de alocação primitiva e são as funções new
e make
. As quais tem propostas diferentes e se aplicam a diferentes tipos, isso pode se tornar confuso porém uma vez entendidas as regras, se torna algo simples.
New
New
é uma função nativa que aloca memória, entretanto, apesar do nome representar algo diferente em outras linguagens, aqui, o New
não inicializa um tipo e um tamanho em um espaço de memória, ele apenas escreve um endereço que contenha um valor zerado. Logo, quando fazemos v := new(T)
o método aloca um espaço de memória do tipo *T
com o valor zero e v
retorna o endereço desse ponteiro.
Uma vez que o valor no endereço retornado pelo new
está zerado, isso se torna útil para modelar e inicializar suas estruturas de dados, onde, cada tipo da sua estrutura é preenchido pelo conceito do valor zerado e não se faz necessário uma inicialização mais complexa. Por exemplo, na documentação de bytes.Buffer
é reportado The zero value for Buffer is an empty buffer ready to use.
Ou seja, o valor de zero para um tipo Buffer inicializado é um buffer vazio pronto para ser usado. De maneira parecida, sync.Mutex
não tem um construtor exposto ou um método inicializador, ao invés disso, o valor zero para sync.Mutex
define um estado unlocked
para a Mutex em questão.
O inicializar um tipo zerado é útil para quando o contexto funciona de maneira mutável. Considere o tipo abaixo
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}
Os valores do tipo SyncedBuffer
estão prontos para serem usados imediatamente com a alocação ou apenas declaração. Abaixo, tanto p
quanto v
funcionarão corretamente sem qualquer tipo de declaração adicional.
p := new(SyncedBuffer) // return *SyncedBuffer
var v SyncedBuffer // return SyncedBuffer
Make
Make(T, args)
é uma função nativa que tem uma proposta diferente de New(T)
. Se concentrando apenas em criar slices
, maps
e channels
. Estes tem por retorno um valor inicializado, todavia, o valor não é zerado e seu tipo se concentra em T
e não *T
.
A razão pela qual existe essa diferença está na representação que esses três tipos representam, por debaixo dos panos, referências a estruturas de dados precisam ser necessáriamente inicializadas antes de serem utilizadas. Um slice
, por exemplo, é um three-item descriptor
uma representação de três partes que contém um ponteiro para o dado que está dentro do próprio array, o comprimento da parte recortada e a capacidade da parte recortada, até que todos esses três parametros sejam estabelecidos slice
tem valor nil
.
Para slices
, maps
e channels
, make
inicializa a estrutura de dados e prepara os valores para serem utilizados, por exemplo
make([]int, 10, 100)
O código acima aloca um array com 100 inteiros e cria um slice
com tamanho 10 e capacidade de 100, apontando para os primeiros 10 elementos do array. Quando você faz um slice
, a capacidade pode ser omitida, entretanto, new([]int)
retorna um ponteiro para um novo endereço, com um slice
zerado, à isso chamamos a pointer to a nil slice value
ou um ponteiro para um slice de valor anulado.
These examples illustrate the difference between new and make.
var p *[]int = new([]int) // *p == nil, ponteiro para slice de valor anulado
var v []int = make([]int, 100) // Slice v se torna um novo array de 100 inteiros
// Complexo demais
var p *[]int = new([]int)
*p = make([]int, 100, 100)
// Recomendado
v := make([]int, 100)
Lembre-se de que o make
se aplica apenas as estruturas citadas e não retorna um ponteiro. Para obter o ponteiro alocado você precisa utiliza new
ou pegar o endereço da variável que recebe o valor da função.
Subscribe to my newsletter
Read articles from Daniel Suhett directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Daniel Suhett
Daniel Suhett
"Enquanto viver, continue aprendendo a viver." Seneca