let vs. var em estruturas e classes

TL;DR: Instâncias constantes de estruturas (criadas com let) não podem ser modificadas de forma alguma, mesmo que algumas de suas propriedades sejam declaradas como var.  Por outro lado, o uso de let em objetos não impede que esses objetos sejam modificados — apenas impede a atribuição a um outro objeto.

O programador de Swift pode ficar um pouco confuso sobre o comportamento de variáveis (declaradas com var) e de constantes (declaradas com let), especialmente se já tem alguma experiência com ferramentas equivalentes em outras linguagens, como o const em C++.

Vamos começar com um exemplo, onde uma classe e uma estrutura são declaradas de forma idêntica, com as mesmas propriedades e inicializador:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Estrutura {
    let constante: Int
    var variavel: Int

    init(constante: Int, variavel: Int) {
        self.constante = constante
        self.variavel = variavel
    }
}

class Classe {
    let constante: Int
    var variavel: Int

    init(constante: Int, variavel: Int) {
        self.constante = constante
        self.variavel = variavel
    }
}

O que pode ser espantoso é a diferença de tratamento que o Swift dá para duas variáveis declaradas desses dois tipos:

1
2
3
4
5
6
7
8
let objeto = Classe(constante: 10, variavel: 20)
// A atribuição a seguir é permitida:
objeto.variavel = 100

let estrutura = Estrutura(constante: 10, variavel: 20)
// A atribuição a seguir é inválida e gera o seguinte erro:
// Cannot assign to property: 'estrutura' is a 'let' constant
estrutura.variavel = 100

Por que somente a última atribuição é inválida, se ela é idêntica à primeira?

De volta às definições

Na verdade, é mais simples do que parece.  Vamos retornar à definição de variáveis e constantes: “Variável é uma entidade que pode ser modificada após sua criação; constante é uma entidade que se mantém inalterada após a criação.”

Se você der uma olhada no post sobre classes e structs, você verá que uma variável cujo tipo é uma classe armazena apenas uma referência para aquele objeto, não o objeto em si.  Isso é um comportamento específico de classes e objetos em Swift; em todos os outros casos — incluindo todos os tipos básicos, structs, enums, etc. —, a variável armazena o valor em si, não uma referência.  Tudo isso é exatamente a mesma coisa para constantes.

Assim, a declaração let estrutura = Estrutura(...) significa: “O valor criado para a constante de nome estrutura não poderá mais ser modificado.” Por isso, é impossível modificar o valor de um de seus campos, pois isso significaria mexer nessa constante.

Só que isso é bem diferente no outro caso. Vejamos: A declaração let objeto = Classe(...) significa: “Crie um objeto dessa classe (esse objeto pode ser modificado depois); além disso, a constante de nome objeto guardará uma referência para essa instância enquanto existir: não poderá posteriormente fazer referência a outro objeto.”

Deu pra entender a diferença?  Tudo se resume ao fato de que objetos são sempre armazenados por referência.  Uma referência constante não significa que o objeto também seja.  A única coisa que é realmente imutável são as propriedades declaradas com let — portanto, a propriedade que chamei de constante nunca pode ser modificada, independente de ser struct ou classe.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.