Embora Rust forneça seu livro Rust, tive dificuldade em entender a palavra-chave mut
. Eu me perguntei: "Sou o único que tem esse problema?" Uma rápida pesquisa no Google confirmou que eu não estava sozinho.
Como resultado, decidi escrever este artigo para fornecer uma explicação detalhada da palavra-chave mut
. Este artigo é destinado a quem vem de linguagens de alto nível, como Python ou JavaScript.
Para criar uma variável imutável em Rust, basta escrever let x = 1337
. É simples. Se você deseja criar uma variável que possa sofrer mutação posteriormente, basta adicionar a palavra-chave mut
após let
. Rust tem uma convenção útil que incentiva a clareza de intenções.
Adicionar a palavra-chave mut
informa aos outros que esta variável será modificada em algum outro lugar do código. OK.
Vamos visualizar isso. Duas variáveis aqui, let mut x = 1337
e let y = 42
.
No momento, tudo é simples. No entanto, as coisas começam a ficar um pouco complicadas ao usar referências mut
. Vamos criar alguns.
let mut x = 1337; let y = 42; let x_ref = &mut x; let y_ref = &y;
Criei duas referências (ou "emprestadas" em termos de Rust). Uma delas é uma referência mutável e a outra é uma referência somente leitura. Vamos criar um esquema para isso novamente.
No esquema fornecido, tenho 4 variáveis, 2 das quais são referências. Ambas as variáveis de referência são imutáveis e não possuem a palavra-chave mut
após let
, o que significa que não posso alterar o que elas apontam. No entanto, ainda posso alterar o valor que eles fazem referência.
*x_ref = 777;
Se você escrever isso, o compilador Rust não reclamará e o valor de x
(não a referência em si) mudará para 777
. No entanto, há um quadrado vermelho no esquema indicando que x_ref
não possui a opção de mutabilidade. Então, por que posso alterar o valor a que se refere?
Vamos voltar ao esquema let x_ref = &mut x
.
O primeiro bloco branco contém o nome: x_ref
. O segundo me informa sobre o tipo armazenado naquela variável. Na sua forma completa, sem quaisquer anotações de tipo implícitas, posso escrever o seguinte:
let x_ref: &mut i32 = &mut x;
Posso interpretar isso como: vamos criar uma variável imutável chamada x_ref
que manterá uma referência mutável para i32
e inicializá-la imediatamente com a referência mutável para o valor i32
na variável x
.
Isso significa que posso modificar o valor para o qual ele aponta, mas não posso alterar o valor (ou endereço) da referência. Em outras palavras, não posso escrever algo como:
let x_ref: &mut i32 = &mut x; let mut z = 0; x_ref = &mut z; // Not allowed!
Em termos de esquemas, quero mudar a direção para a qual a seta aponta no bloco de código acima. Porém, mesmo que a variável z
seja mutável, não posso alterar a seta porque o problema está na imutabilidade do próprio x_ref
.
Para alterar a direção da seta, preciso modificar o endereço armazenado na variável x_ref
. No entanto, não posso fazer isso porque a variável é imutável.
Vamos fazê-lo!
let mut x: i32 = 1337; let mut x_ref: &mut i32 = &mut x; // I've added mut before x_ref let mut z = 0; x_ref = &mut z; // Allowed!
Existem muitas instâncias de mut
aqui em torno de x_ref
, certo? Vamos descrevê-los.
let mut x_ref
: estou criando uma variável mutável chamada x_ref
, o que significa que posso alterar seu valor mais tarde.
&mut i32
: estou afirmando que a variável conterá referências mutáveis para algum valor do tipo i32
.
&mut x
: estou pegando emprestada (obtendo uma referência) a variável x
.
Então, criei uma variável chamada z
e atribuí a ela o valor 0
. Posteriormente, quando escrevi x_ref = &mut z
, indiquei que entendo x_ref
como uma variável mutável que só pode conter referências a valores i32
.
Como o tipo de z
é i32
, posso atribuir seu endereço à variável x_ref
. Para obter o endereço de z
, usei a sintaxe &mut z
.
O esquema.
Dê uma olhada em =
na afirmação, pode parecer um pouco óbvio, mas…
let mut x_ref = &mut x;
… Eu vejo isso como um divisor (especialmente se você girar 90 graus) que divide a afirmação em duas subdeclarações: esquerda e direita. O lado esquerdo fornece informações sobre a própria variável , enquanto o lado direito nos informa sobre o valor .
Quando uso o operador *
dereference para alterar o valor...
*x_ref = 100;
... Eu não altero o valor da variável x_ref
. Em vez disso, estou alterando o valor ao qual x_ref
está referenciando.
Eu usei mut
com frequência antes. E se eu omitir alguns deles?
let i = 1; let j = 2; let mut k = &i;
Posso alterar o valor de i
aqui? Usando a técnica do divisor, é bastante simples responder. Posso alterar o valor de k
(vejo mut
no lado esquerdo), mas o valor (lado direito) é uma referência imutável a i
(não há mut
aqui).
Portanto…
let i = 1; let j = 2; let mut k = &i; k = &j; // This is legal. *k = 3; // This is not.
O esquema.
Neste artigo, dissecamos as nuances da palavra-chave e das referências mut
. Lembre-se, há uma distinção entre uma referência mutável e uma variável mutável que contém uma referência. Nosso truque?
Usando o sinal =
como divisor mental para entender melhor as atribuições no Rust. Esta simples visualização pode esclarecer muitas confusões.
Boa codificação!