Lições do módulo (2/2)
Referências e empréstimos
No Rust, transferir propriedade de funções pode ser entediante. Para resolver isso, Rust usa referências (&).
A criação de uma referência a um valor é chamada de Empréstimo.
Referências imutáveis
Uma referência é declarada prefixando o tipo ou variável com o símbolo &. Por padrão, as referências são imutáveis: permitem ler o valor, mas não modificá-lo.
fn main() {
let s1 = String::from("hello");
// We pass a reference to s1, not ownership
let len = calculate_length(&s1);
// s1 is still usable here!
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize { // s is a reference to a String
s.len()
} // Here, s goes out of scope, but since it doesn't own the value, nothing happensReferências mutáveis
Se precisar modificar um valor emprestado, você deverá usar uma referência mutável com &mut. A variável original também deve ser declarada mutável com mut:
fn main() {
let mut s = String::from("hello");
// We pass a mutable reference
change(&mut s);
println!("{}", s); // Prints "hello, world"
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}As regras do empréstimo
Para evitar corrupção de memória e corridas de dados em tempo de compilação, Rust impõe duas regras fundamentais:
- Você pode ter qualquer número de referências imutáveis (
&T) para um valor ao mesmo tempo. - OU você pode ter exatamente uma referência mutável (
&mut T) para um valor por vez.
Você absolutamente não pode misturar referências imutáveis e mutáveis para o mesmo valor no mesmo escopo:
let mut s = String::from("hello");
let r1 = &s; // Valid
let r2 = &s; // Valid
// let r3 = &mut s; // COMPILATION ERROR! You cannot create &mut s if s is already borrowed as immutableEscopo de referência e tempos de vida não lexicais (NLL)
Historicamente, o escopo de uma referência durava até o final do bloco em que foi criada. Hoje, o compilador do Rust é mais inteligente graças ao Non-Lexical Lifetimes (NLL): o escopo de uma referência termina na última linha em que é realmente usada, não necessariamente no final do bloco.
Isso torna o seguinte código perfeitamente válido:
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2); // Last use of r1 and r2. The immutable references expire here!
let r3 = &mut s; // Valid! No active immutable references exist at this pointExperimente você mesmo
Passe uma referência imutável de s1 para a função calcula_comprimento usando o símbolo &.
Mostrar dica
Substitua `/* TODO \_/`com`&s1` para passar uma referência imutável para a string.
Solução disponível após 3 tentativas
Torne a variável s mutável (let mut s) e passe uma referência mutável (&mut s) para a função change para permitir que ela modifique a string.
Mostrar dica
Use `let mut s` em vez de`let s`e chame`change(&mut s);`.
Solução disponível após 3 tentativas
Declare a variable s containing String::from("Rust"). Create two distinct immutable references r1 and r2 to s, and finally print both references separated by a space using the println! macro.
Mostrar dica
Assign `let r1 = &s;` and `let r2 = &s;` to obtain two immutable references, then print them with `println!("{} {}", r1, r2);`.
Solução disponível após 3 tentativas