A diferença entre array e slice

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.


Subscreva

Fique por dentro de tudo o que acontece no mundo Go.

Deixe uma resposta