Rust: Referências e empréstimos no Rust
Última atualização: 2026-08-26
Uma citação é como um cartão de biblioteca — você pode pegá-la emprestada (citação somente para leitura) ou solicitar permissão exclusiva para reescrevê-la (citação editável), mas não pode fazer as duas coisas ao mesmo tempo.
O mecanismo de referência do Rust permite acessar dados sem transferir a propriedade. Isso é chamado de “empréstimo”.
1. O que você vai aprender
- Use
&Tpara criar uma referência imutável (empréstimo somente para leitura) - Criar uma referência mutável (empréstimo de leitura e gravação) usando
&mut T - Regras de empréstimo: uma referência mutável ou várias referências imutáveis (não ambas ao mesmo tempo)
- O escopo das referências e os NLL (tempos de vida não lexicais)
- Referências pendentes e medidas de segurança do compilador
- Aplicações de referências em parâmetros de função
2. Diagramas conceituais
flowchart LR
subgraph "Transfer of Ownership"
A1["Data String"] -->|"let s2 = s1<br>move"| B1["New Owner s2"]
B1 -->|"s1 Failure"| C1["Compilation Error<br>println!(s1)"]
end
subgraph "Citation, Borrowing"
A2["Data String"] -->|"let r = &s<br>borrow"| B2["Quote r"]
B2 -->|"r Return after use<br>s Still valid"| C2["✅ Secure Access"]
end
3. A história de uma biblioteca
(1) Desgosto: O livro foi rasgado em pedaços
Há um livro popular intitulado Programação em Rust na biblioteca do Tom, e ele só pode ser emprestado uma vez:
- Alice pegou este livro emprestado, e a propriedade foi transferida para Alice
- Bob queria ler o livro, mas ele já não estava mais na biblioteca — pertencia a apenas uma pessoa.
- A biblioteca terá que comprar outro exemplar.
“Se ao menos eu pudesse deixar várias pessoas lerem o mesmo livro ao mesmo tempo sem que elas o destruíssem...”
(2) Soluções para referências em Rust
fn main() {
let book = String::from("Rust Programming");
// Multiple immutable references: Multiple people at the same time "read-only" check out
let reader1 = &book; // Reader 1 checks out (read-only)
let reader2 = &book; // Reader 2 checks out (read-only)
println!("Readers 1 See: {}", reader1);
println!("Readers 2 See: {}", reader2);
// Both references are valid at the same time -- because everyone has read-only access
let mut notebook = String::from("Notebook");
// Mutable References: write-only permission
let writer = &mut notebook; // Only one person can make changes
writer.push_str("-- I wrote some notes");
println!("Rewriter: {}", writer);
// writer ends scope here, only then can others borrow it again.
}
As regras de citação são como as regras de uma biblioteca: Ou várias pessoas podem ler ao mesmo tempo (múltiplo &T), ou uma pessoa pode editar sozinha (um &mut T) — mas isso não pode acontecer simultaneamente.
4. Regras para empréstimos
graph TB
A[Borrowing Rules] --> B[You can only choose one of them.]
B --> C[Multiple immutable references &T]
B --> D[Or a mutable reference &mut T]
B --> E[Cannot coexist]
A --> F[References must always be valid.]
F --> G[There must be no dangling references.]
| Regra | Descrição | Consequências da violação |
|---|---|---|
| Várias referências &T ou uma única referência &mut T | Referências imutáveis e mutáveis não podem coexistir | Erro de compilação |
| As referências devem ser válidas | Uma referência não pode permanecer ativa após o objeto ao qual se refere ter sido destruído | Erro de compilação |
| Quando uma referência é imutável, o valor original também não pode ser modificado | Quando &T existe, o valor original não pode ser modificado por meio de &mut T | Erro de compilação |
| Âmbito da NLL | O âmbito de &mut T termina após seu último uso | Otimização orientada pelo compilador |
(2) Comparação entre tipos de referência
| Tipo de citação | Sintaxe | Permissões | Número máximo permitido | Usos típicos |
|---|---|---|---|---|
| Referência imutável | &T |
Somente leitura | Múltipla | Acesso somente leitura aos parâmetros da função |
| Referência da variável | &mut T |
Leitura/gravação | 1 | O parâmetro da função modifica os dados |
| Ponteiro de fat (fatia) | &[T] |
Somente leitura | Múltiplo | Fatia de array/string |
(3) Referência rápida para cenários de empréstimo
| Cenário | Abordagem recomendada | Exemplo |
|---|---|---|
| Acesso à função somente leitura | &T |
fn len(s: &String) -> usize |
| Funções que modificam dados | &mut T |
fn push(s: &mut String, ch: char) |
| Uso de dados da função | T (passagem por valor) |
fn consume(s: String) |
| Dados de retorno da função | T (Valor de retorno) |
fn create() -> String |
| Várias funções compartilham acesso somente leitura | &T |
Várias funções recebem o mesmo &T |
| Modificações alternadas | &mut T (Multiplexação por divisão de tempo) |
O NLL permite a criação alternada de &mut T |
5. Exemplos de citações
▶ Exemplo 1: Referências imutáveis — Acesso somente para leitura (Dificuldade ⭐⭐)
// ============================================
// Immutable References &T: Multiple readers can borrow items at the same time
// ============================================
fn calculate_length(s: &String) -> usize {
// s is a String reference, no ownership
s.len()
} // s goes out of scope, but since it's a reference, it will not destroy the String
fn main() {
let s = String::from("Hello, Rust!");
let len = calculate_length(&s); // Borrow s, no transfer of ownership
println!("'{}' The length of is: {}", s, len); // ✅ s Still available
// Multiple immutable references can coexist
let r1 = &s;
let r2 = &s;
println!("r1: {}, r2: {}", r1, r2); // ✅ Read simultaneously
}
Resultado:
'Hello, Rust!' The length of is: 12
r1: Hello, Rust!, r2: Hello, Rust!
&scria uma referência as; quando passada para uma função, a propriedade não é transferida. Após o retorno da função, a referência se torna inválida, massainda existe. Isso é o que se conhece como “empréstimo” — você usa e depois devolve.
▶ Exemplo 2: Referências mutáveis — Permissões exclusivas de modificação (Dificuldade ⭐⭐)
// ============================================
// Mutable References &mut T: Only one writer at a time
// ============================================
fn main() {
let mut s = String::from("Hello");
// Create a mutable reference
let r = &mut s;
r.push_str(", world!");
println!("Modifying Through a Variable Reference: {}", r);
// r is used here for the last time, NLL ends
// After the mutable reference ends, you can create a new mutable reference.
let r2 = &mut s;
r2.push_str("!!");
println!("Another mutable reference: {}", r2);
// --- Examples of Errors (Uncomment to view compilation errors) ---
// let mut s2 = String::from("test");
// let ref1 = &mut s2;
// let ref2 = &mut s2; // ❌ There cannot be two mutable references at the same time.
// println!("{}, {}", ref1, ref2);
}
Resultado:
Modifying Through a Variable Reference: Hello, world!
Another mutable reference: Hello, world!!!
&muté mutável — o valor da referência pode ser modificado. No entanto, o Rust impõe a regra de que apenas uma referência mutável pode existir por vez. Isso evita corridas de dados — não são necessários bloqueios nem operações atômicas; a questão é resolvida inteiramente em tempo de compilação.
▶ Exemplo 3: Referências imutáveis e mutáveis não podem coexistir (Dificuldade ⭐⭐⭐)
// ============================================
// Demonstration of Conflicts Between Immutable and Mutable References
// ============================================
fn main() {
let mut data = String::from("Key Data");
let r1 = &data; // ✅ Immutable References 1
let r2 = &data; // ✅ Immutable References 2
println!("r1: {}, r2: {}", r1, r2);
// r1 and r2 are used here for the last time
let r3 = &mut data; // ✅ At this point, you can create a mutable reference.
r3.push_str("--Modified");
println!("r3: {}", r3);
// --- Examples of Errors: A mutable reference cannot be created while an immutable reference still exists. ---
// let mut s = String::from("Demo");
// let r_a = &s; // Immutable References
// let r_b = &mut s; // ❌ Compilation Error: immutable reference already exists
// println!("{}, {}", r_a, r_b);
}
Resultado:
r1: Key Data, r2: Key Data
r3: Key Data--Modified
Ponto-chave: As referências imutáveis
r1er2saem do escopo após seu último uso (println!), e só então a referência mutávelr3pode ser criada. Isso é o NLL (Non-Lexical Lifetime) — o compilador determina de forma inteligente quando uma referência não está mais em uso, em vez de esperar pelo fim do bloco.
▶ Exemplo 4: Como evitar referências pendentes (Dificuldade ⭐⭐⭐)
// ============================================
// How Do Compilers Prevent Dangling References?
// ============================================
// Examples of Errors: Return a reference to a local variable
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // ❌ Compilation Error: s is destroyed at the end of the function, the reference would be dangling
// }
// The Correct Approach: Return String directly (transfer of ownership)
fn no_dangle() -> String {
let s = String::from("hello");
s // Return String directly, ownership is transferred to the caller
}
fn main() {
let s = no_dangle();
println!("Safe Return Values: {}", s);
// The Correct Way to Reference Local Variables: Use within the scope
let local = String::from("Local Data");
let r = &local; // Quote
println!("Referencing Local Variables: {}", r);
// r ends here, local is still valid
} // local is destroyed here (later than r), safe
Resultado:
Safe Return Values: hello
Referencing Local Variables: Local Data
Uma referência pendente ocorre quando a memória apontada por uma referência foi desalocada. O compilador do Rust consegue detectar essa situação em tempo de compilação — se o objeto apontado por uma referência for destruído após a criação da referência, o compilador reportará um erro. Isso elimina completamente bugs como os “ponteiros pendentes”.
▶ Exemplo 5: Exercício abrangente — Operações com contas bancárias (Dificuldade ⭐⭐⭐)
// ============================================
// Comprehensive Example: Applications of References in Real-Life Scenarios
// ============================================
struct Account {
owner: String,
balance: f64,
}
fn check_balance(account: &Account) -> String {
format!("{} balance: {:.2} yuan", account.owner, account.balance)
}
fn deposit(account: &mut Account, amount: f64) {
if amount <= 0.0 {
println!("The deposit amount must be greater than 0");
return;
}
account.balance += amount;
println!("Deposit {:.2} Yuan Chenggong", amount);
}
fn transfer(from: &mut Account, to: &mut Account, amount: f64) -> bool {
if amount <= 0.0 || from.balance < amount {
println!("Transfer Failed: Insufficient balance or invalid amount");
return false;
}
from.balance -= amount;
to.balance += amount;
println!("Transfer {:.2} yuan: {} -> {}", amount, from.owner, to.owner);
true
}
fn show_accounts(accounts: &[Account]) {
println!("--- Account List ---");
for (i, acc) in accounts.iter().enumerate() {
println!("{}. {} balance: {:.2} yuan", i + 1, acc.owner, acc.balance);
}
}
fn main() {
let mut alice = Account { owner: String::from("Alice"), balance: 1000.0 };
let mut bob = Account { owner: String::from("Bob"), balance: 500.0 };
println!("{}", check_balance(&alice));
println!("{}", check_balance(&bob));
deposit(&mut alice, 200.0);
println!("{}", check_balance(&alice));
transfer(&mut alice, &mut bob, 300.0)?;
let accounts = [&alice, &bob];
show_accounts(&accounts);
let total: f64 = accounts.iter().map(|a| a.balance).sum();
println!("Total Deposits: {:.2} yuan", total);
}
Resultado:
Alice balance: 1000.00 yuan
Bob balance: 500.00 yuan
Deposit 200.00 Yuan Successful
Alice balance: 1200.00 yuan
Transfer 300.00 yuan: Alice -> Bob
--- Account List ---
1. Alice balance: 900.00 yuan
2. Bob balance: 800.00 yuan
Total Deposits: 1700.00 yuan
Este exemplo demonstra a aplicação prática dos três tipos de referência:
&T(consulta somente leitura),&mut T(operação de modificação) e&[T](percorro somente leitura).transferrequer duas referências&mut Tsimultaneamente, o que é seguro em Rust, pois elas apontam para dados diferentes.
❓ Perguntas Frequentes
P: Qual é a diferença entre uma referência e um ponteiro? R: Uma referência é um ponteiro cuja validade é garantida. As referências nunca são nulas e sempre apontam para uma memória válida. Em C, um
int*pode ser NULL ou “dangling”, mas em Rust, um&i32tem validade garantida.
P: &T e &mut T podem coexistir? R: Não. Esse é um princípio fundamental das regras de empréstimo do Rust. É possível ter várias referências somente para leitura (&T) ou uma única referência para leitura e gravação (&mut T), mas não ambas ao mesmo tempo. Isso garante que não ocorram problemas relacionados à “leitura de dados alterados”.
P: O que é NLL (Non-Lexical Lifetime)? R: O NLL permite que as referências expirem após seu último uso, em vez de esperar até o final do bloco. Isso reduz significativamente a rigidez do código. Por exemplo, após
let r = &mut x; r.push(1);,rexpira, e você pode criar imediatamente uma nova referência mutável.
P: Quando se deve usar uma referência em vez de passar por propriedade? R: Use uma referência quando precisar apenas “dar uma olhada” nos dados, e use a propriedade quando precisar “possuir” ou “manter” os dados a longo prazo. Use referências para parâmetros de função sempre que possível; dessa forma, quem chama a função não precisa se preocupar com a perda da propriedade.
P: É possível armazenar uma referência em uma estrutura? R: Sim, mas é preciso especificar um parâmetro de tempo de vida. Isso está na Lição 11. Em termos simples, quando uma estrutura contém uma referência, é preciso informar ao compilador “por quanto tempo essa referência permanecerá ativa”.
📖 Resumo
- Referência permite acessar dados sem transferir a propriedade — ou seja, “tomando-os emprestados”.
- Referência imutável
&T: Podem existir várias instâncias simultaneamente; somente leitura - Referência variável
&mut T: Apenas uma por vez; leitura/gravação - Regras de empréstimo: Vários
&Tou um único&mut Tnão podem coexistir - As referências devem sempre ser válidas; o compilador impede referências pendentes
- O NLL faz com que uma referência seja liberada após seu último uso, em vez de esperar até o final do bloco
📝 Exercícios
- Dificuldade ⭐: Escreva uma função
fn print_message(msg: &String)que exiba a string que lhe for passada. Emmain, crieStringe, em seguida, chame essa função para verificar se a variável original continua acessível após a chamada. - Dificuldade ⭐⭐: Escreva um programa para criar um
mut String. Primeiro, crie duas referências imutáveis e exiba-as; em seguida, crie uma referência mutável, modifique seu conteúdo e exiba o resultado modificado. - Dificuldade ⭐⭐⭐: Tente escrever uma função que retorne uma referência (como
fn get_ref() -> &String) e observe as mensagens de erro do compilador. Em seguida, modifique-a para que retorne a propriedade deStringe entenda por que o Rust proíbe o retorno de referências a variáveis locais.