Quando comecei aprender Go, eu achava que slice e array eram a mesma coisa. Depois de algum tempo que fui descobrir e entender a diferença entre eles.
Nesse post vou falar sobre a diferença entre eles e refatorar o código do vídeo sobre bubble sort que publicamos lá no canal, para que ele funcione com arrays ao invés de slice.
Para começar, vamos entender o que são arrays.
Um array é um tipo de dado composto, de tamanho fixo com zero ou mais elementos de um tipo específico. Por ter tamanho fixo, raramente vemos o uso de arrays em programas Go.
Existem três formas básicas para se iniciar um array.
var a [5]int32 b := [5]int32{1, 2, 3, 4, 5} c := [...]int32{1, 2, 3, 4, 5}
O uso de reticências faz com que o compilador determine seu tamanho no momento do build (sim eu quis dizer build).
O tamanho de um array faz parte do seu tipo, ou seja, no exemplo acima a variável a
só aceitaria a atribuição de uma lista [5]int32
. Caso em algum ponto fosse feita a atribuição de um [6]int32
, um erro de compilação seria exibido ao tentar fazer o build do programa.
Outro item interessante sobre array, é que eles são comparáveis, como mostramos no exemplo abaixo.
a := [3]int{1, 5, 20} b := [...]int{1, 5, 20} fmt.Println(a == b) // saída será: true
No entanto, para que isso funcione, ambos os arrays devem ser do mesmo tipo. Comprar um array do tipo [3]int
com um array do tipo [4]int
irá gerar um erro de compilação.
Quando definimos um array como parâmetro, a menos que digamos que ele é um ponteiro, o valor passado para a função será o valor do array, ou seja, qualquer mudança que a função faça no array não será refletida fora da função.
No caso da função bubbleSort que implementamos no vídeo sobre algoritmos, se refatorarmos ela para aceitar somente arrays do tipo [6]int
, o nosso fmt.Println(unsorted)
da função principal, para de exibir o array ordenado e passa a exibir sua versão original.
Para ter acesso ao array ordenado, temos que atribuir o retorno da função a alguma variável.
package main import "fmt" func bubbleSort(n [6]int) [6]int { for i, _ := range n { for j := 0; j < len(n)-i-1; j++ { if n[j] > n[j+1] { n[j], n[j+1] = n[j+1], n[j] } } } return n } func main() { unsorted := [...]int{1, 7, 8, 5, 3, 9} sorted := bubbleSort(unsorted) fmt.Println(unsorted) // [1 7 8 5 3 9] fmt.Println(sorted) // [1 3 5 7 8 9] }
Agora que definimos o que são array, vamos falar um pouco sobre slices.
Assim como array, um slice é um tipo composto de tamanho variável cujos elementos são do mesmo tipo. Diferente do array, um slice não tem um tamanho fixo, o que o faz ser muito mais útil, de modo geral, do que o array.
Um slice é composto por três componentes básicos. Um ponteiro, seu tamanho e sua capacidade. Podemos inicializar um slice de quatro formas.
var a []int32 b := []int32{1, 5, 3, 6} c := make([]int32, 4) // define somente tamanho d := make([]int32, 0, 10) // define tamanho e capacidade
Como um slice contém um ponteiro para um array interno, passar um slice como parâmetro de uma função permite que a função modifique os elementos do slice.
Por isso, no código original que fizemos na implementação do bubble sort, quando passamos o slice para a função, teoricamente não precisamos receber seu retorno, já que as mudanças já se refletiram no slice original.
Um cuidado muito grande que devemos tomar, é que se dois slices apontarem para o mesmo array, qualquer um deles que tenha seu valor modificado irá refletir no outro.
slice := []int{1, 2, 3, 4, 5} a := slice[:3] b := slice[2:] slice[2] = 99 fmt.Println(a, b) // [1 2 99] [99 4 5]
Diferente dos arrays, slices não são comparáveis utilizando == ou !=. Para comparar um slice, é preciso fazer um looping e comparar manualmente item por item.
Deixem suas dúvidas nos comentários.
Até a próxima.