paint-brush
Referências mutáveis ou variáveis mutáveis? Decodificando o mut-mut-mut do Rustpor@charnog
883 leituras
883 leituras

Referências mutáveis ou variáveis mutáveis? Decodificando o mut-mut-mut do Rust

por Denis Gonchar5m2023/09/23
Read on Terminal Reader

Muito longo; Para ler

A segurança da memória do Rust está enraizada em seus mecanismos de propriedade e empréstimo. Este artigo investiga as complexidades da palavra-chave mut, distinguindo entre referências mutáveis e variáveis mutáveis. A principal lição? Nem todos os muts são criados iguais: referências mutáveis permitem alterar o valor apontado, enquanto variáveis mutáveis com referências podem mudar para onde apontam. Um truque útil de visualização usando o sinal = simplifica a compreensão dessas distinções.
featured image - Referências mutáveis ou variáveis mutáveis? Decodificando o mut-mut-mut do Rust
Denis Gonchar HackerNoon profile picture

Introdução

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.

As Variáveis

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 .

Primeiro nome; segundo tipo, terceiro valor, quarto sinalizador mut.

As referências

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.


  1. let mut x_ref : estou criando uma variável mutável chamada x_ref , o que significa que posso alterar seu valor mais tarde.


  2. &mut i32 : estou afirmando que a variável conterá referências mutáveis para algum valor do tipo i32 .


  3. &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.

O truque mental

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.

Referências imutáveis

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.

Conclusão

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!