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:
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:
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.