Rust: Propriedade em Rust
Última atualização: 2026-08-26
A propriedade é a característica mais marcante do Rust — ela permite que o Rust garanta a segurança da memória sem a necessidade de um coletor de lixo.
A propriedade é a diferença mais fundamental entre o Rust e outras linguagens. Quando você compreende isso, compreende a essência do design do Rust.
1. O que você vai aprender
- Três princípios de propriedade: cada valor tem exatamente um proprietário
- Os valores são liberados (descartados) automaticamente quando saem do escopo
- Significado de “transferência”: transferência de propriedade
- A diferença entre as características
CopyeClone - Métodos de armazenamento de dados para pilhas e heaps
- Transferência de propriedade em chamadas de função
2. Diagramas conceituais
flowchart LR
A["Value Creation<br>let s = String::from(...)"] --> B["Owner Binding<br>s Has this value"]
B --> C["Transfer of Ownership move<br>let s2 = s; s Failure"]
C --> D["Out of scope drop<br>} Automatic Memory Release"]
D --> E["Memory Security<br>No Double Release,Non-swinging pointer"]
3. A história de uma biblioteca
(1) A agonia: o livro foi emprestado por duas pessoas ao mesmo tempo
Tom administra uma pequena biblioteca. Ele se deparou com um problema clássico:
- Alice pegou emprestado um exemplar do livro Programação em Rust e leu até a página 50.
- Bob queria o mesmo livro, então Tom o emprestou a ele de novo.
- No fim das contas, Alice escreveu uma anotação na página 50, e Bob escreveu uma anotação na página 100.
- Quando o livro foi finalmente devolvido, as anotações estavam todas misturadas, e era impossível saber quem as havia escrito.
- Para piorar a situação, duas pessoas editaram a mesma página ao mesmo tempo, o que fez com que a página ficasse fragmentada.
“Se ao menos um livro pudesse ser emprestado por apenas uma pessoa por vez...”
(2) Regras de propriedade do Rust
fn main() {
let book = String::from("Rust Programming"); // book is the owner of this book
// let book2 = book; // ❌ If you write it this way,book Ownership was transferred to book2
// println!("{}", book); // ❌ book Can no longer be used
// ✅ The Correct Approach:Only one person can hold ownership at a time.
println!("{} Belongs to Tom The Library", book);
} // book Automatically destroyed here——No manual intervention required free
As regras de propriedade do Rust são como a política de “uma pessoa por vez” de uma biblioteca: cada valor tem exatamente um proprietário a qualquer momento. Quando o proprietário sai do escopo, o valor é automaticamente destruído.
4. Os três princípios da propriedade
graph TB
A[The Three Principles of Ownership] --> B[Principles1: Each value has exactly one owner.]
A --> C[Principles2: It is destroyed as soon as it leaves its scope.]
A --> D[Principles3: Ownership can be transferred(move)]
B --> E[let s = String::from("hi")]
C --> F[} Automatically call at the end drop]
D --> G[let s2 = s; // s No longer valid]
(1) Explicação detalhada dos princípios
| Princípio | Descrição | Analogia |
|---|---|---|
| Único proprietário | A qualquer momento, apenas uma pessoa é “proprietária” de cada valor | Um livro da biblioteca só pode ser emprestado a uma pessoa por vez |
| Fim do escopo = Destruir | Chamado automaticamente quando uma variável sai do escopo drop |
Os livros são devolvidos automaticamente quando chega a data de devolução |
| Transferência de propriedade (movimento) | A propriedade é transferida durante a atribuição ou a passagem de parâmetros; a variável original passa a ser inválida | Um livro é passado de uma pessoa para outra |
5. Pilhas e montes
Para entender o conceito de propriedade, é preciso primeiro compreender a diferença entre a pilha e o heap:
| Dimensão | Pilha | Heap |
|---|---|---|
| Velocidade de alocação | Muito rápida (push/pop) | Mais lenta (requer a localização de memória livre) |
| Armazenamento de dados | Dados de tamanho conhecido no momento da compilação | Dados de tamanho desconhecido ou que muda dinamicamente |
| Tipos comuns | i32, bool, f64, Array |
String, Vec, Box |
| Gerenciamento de memória | Automático (quadros da pilha de chamadas de função) | Requer gerenciamento manual ou baseado em propriedade |
(3) Comparação entre “Copiar”, “Clonar” e “Mover”
| Operação | Sintaxe | A variável original é válida? | Sobrecarga de desempenho | Tipos aplicáveis |
|---|---|---|---|---|
| Cópia | let b = a; |
Válido | Muito baixo (cópia bit a bit da pilha) | i32, f64, bool, char, tuplas (incluindo tipos de cópia) |
| Clone | let b = a.clone(); |
Válido | Alto (alocação de memória na pilha) | String, Vec<T>, Box<T> |
| Mover | let b = a; |
Expira | 0 (apenas copia o ponteiro) | String, Vec<T>, Box<T> |
Se um tipo implementar
Copy, a atribuição resultará em uma cópia automática; se não implementarCopy, a atribuição resultará em uma transferência automática. Use.clone()para uma cópia profunda explícita quando for necessário preservar a variável original.
fn main() {
// Data on the Stack:Fixed size,Copy Semantics
let x: i32 = 5; // Allocation on the Stack 4 Byte
let y = x; // Make a copy, x and y are both valid.
println!("x={}, y={}", x, y); // ✅ All are acceptable
// Load the data:Varies in size,move Semantics
let s1 = String::from("hello"); // s1 On the stack(ptr/len/cap),The actual data is on the heap.
let s2 = s1; // ❌ Ownership from s1 Transfer to s2
// println!("{}", s1); // ❌ Compilation Error:s1 Has been moved
println!("{}", s2); // ✅ s2 It's the new owner.
}
6. Exemplos de propriedade
▶ Exemplo 1: semântica de “move” — transferência de propriedade (Dificuldade ⭐⭐)
// ============================================
// Demonstration of Ownership Transfer:Occurs during assignment move
// ============================================
fn main() {
let s1 = String::from("Rust");
let s2 = s1; // s1 Ownership was transferred to s2
// println!("s1: {}", s1); // ❌ Compilation Error!s1 Has been moved
println!("s2: {}", s2); // ✅ s2 I am the owner now
// Integer types are Copy, So it won't move
let a = 42;
let b = a; // a The value is copied to b
println!("a: {}, b: {}", a, b); // ✅ Both are effective.
}
Resultado:
s2: Rust
a: 42, b: 42
Tipos escalares, como inteiros, implementam a característica
Copy; portanto, as atribuições envolvem cópia, e não movimentação.Stringnão implementaCopy; portanto, as atribuições envolvem movimentação. Após uma movimentação, a variável original se torna inválida; essa é uma característica fundamental do design do Rust para evitar a “liberação dupla”.
▶ Exemplo 2: Passagem de propriedade em chamadas de função (Dificuldade ⭐⭐)
// ============================================
// Transfer of Ownership in Parameter Passing and Return Values
// ============================================
fn take_ownership(s: String) {
println!("Acquire ownership: {}", s);
} // s Destroyed here(drop)
fn give_ownership() -> String {
let s = String::from("Newly created string");
s // Ownership is returned to the caller
}
fn main() {
let s1 = String::from("hello");
take_ownership(s1); // s1 Ownership was transferred to the function
// println!("{}", s1); // ❌ s1 Expired
let s2 = give_ownership(); // Acquiring Ownership from a Function
println!("s2: {}", s2); // ✅ s2 Ownership
// Send it in and then send it back
let s3 = String::from("Passing back and forth");
let s3_back = takes_and_returns(s3);
// println!("{}", s3); // ❌ s3 Has been moved
println!("s3_back: {}", s3_back); // ✅
}
fn takes_and_returns(s: String) -> String {
println!("Inside a function: {}", s);
s // Return ownership to the caller
}
Resultado:
Acquire ownership: hello
s2: Newly created string
Inside a function: Passing back and forth
s3_back: Passing back and forth
Quando uma função é chamada, a propriedade dos parâmetros passados é transferida para a função. Quando a função retorna um valor, a propriedade é transferida de volta para quem a chamou. É isso que se entende por “fluxo de propriedade entre funções” — é o mesmo princípio de “quem tem a chave pode abrir a porta”.
▶ Exemplo 3: Comparando “Copiar” e “Clonar” (Dificuldade: ⭐⭐⭐)
// ============================================
// Copy(Auto-Copy) vs Clone(Explicit Cloning)
// ============================================
fn main() {
// --- Copy Type:Automatic Copy on Assignment ---
let num1 = 100;
let num2 = num1; // Auto-Copy,num1 Still valid
println!("Copy: num1={}, num2={}", num1, num2); // ✅
// --- Clone Type:Must be explicitly called .clone() ---
let s1 = String::from("Cloning required");
let s2 = s1.clone(); // Explicit Cloning,s1 Still valid
println!("Clone: s1={}, s2={}", s1, s2); // ✅
// --- Neither Clone Nor Copy: only move ---
let v1 = vec![1, 2, 3];
let v2 = v1; // move!v1 Failure
// println!("{:?}", v1); // ❌
println!("v2: {:?}", v2); // ✅
}
Resultado:
Copy: num1=100, num2=100
Clone: s1=Cloning required, s2=Cloning required
v2: [1, 2, 3]
Cópia: Realiza automaticamente uma cópia bit a bit durante a atribuição; a variável original permanece válida (aplica-se a dados na pilha). Clonar: Requer uma chamada explícita ao método
.clone(); aplica-se a dados na pilha. A clonagem é mais dispendiosa (requer alocação de memória na pilha), por isso o Rust foi projetado de forma que “você deva optar explicitamente por clonar”.
▶ Exemplo 4: Padrões para adquirir e devolver a propriedade em funções (Dificuldade ⭐⭐⭐)
// ============================================
// Common Patterns:Send it in and then send it back(Get+Return)
// ============================================
struct User {
name: String,
score: i32,
}
fn boost_score(mut user: User, bonus: i32) -> User {
user.score += bonus;
user
}
fn format_user(user: &User) -> String {
format!("{}: {} pts", user.name, user.score)
}
fn main() {
let alice = User {
name: String::from("Alice"),
score: 80,
};
println!("Before the upgrade: {}", format_user(&alice));
let alice = boost_score(alice, 15);
println!("After the upgrade: {}", format_user(&alice));
let alice = boost_score(alice, 10);
println!("Upgrade Again: {}", format_user(&alice));
let mut bob = User {
name: String::from("Bob"),
score: 60,
};
let bonus = 20;
bob.score += bonus;
println!("Bob After the bonus points are added: {}", format_user(&bob));
let names = vec![alice.name.clone(), bob.name.clone()];
println!("All users: {:?}", names);
println!("Alice Final Score: {}", alice.score);
println!("Bob Final Score: {}", bob.score);
}
Resultado:
Before the upgrade: Alice: 80 pts
After the upgrade: Alice: 95 pts
Upgrade Again: Alice: 105 pts
Bob After the bonus points are added: Bob: 80 pts
All users: ["Alice", "Bob"]
Alice Final Score: 105
Bob Final Score: 80
A propriedade das estruturas é transferida da mesma forma que para os tipos básicos.
boost_scoreA propriedade é adquirida por meio de um parâmetro e devolvida após a modificação — esse é o padrão “adquirir e devolver”. O uso de uma referência (&User) permite o acesso somente para leitura sem adquirir a propriedade.
▶ Exemplo 5: Interação entre propriedade e conjuntos (Dificuldade ⭐⭐⭐)
// ============================================
// Vec,String Common Interaction Patterns with Ownership
// ============================================
fn take_first_word(words: &Vec<String>) -> Option<&str> {
words.first().map(|s| s.as_str())
}
fn remove_and_return(vec: &mut Vec<i32>, index: usize) -> Option<i32> {
if index < vec.len() {
Some(vec.remove(index))
} else {
None
}
}
fn main() {
let mut words = vec![
String::from("hello"),
String::from("rust"),
String::from("world"),
];
println!("Original vector: {:?}", words);
if let Some(first) = take_first_word(&words) {
println!("The First Word: {}", first);
}
let removed = remove_and_return(&mut words, 1);
println!("Remove Index 1: {:?}, Remaining: {:?}", removed, words);
let mut numbers = vec![10, 20, 30, 40, 50];
while let Some(val) = numbers.pop() {
println!("Pop up: {}", val);
}
println!("The vector is empty: {:?}", numbers);
let s1 = String::from("foo");
let s2 = String::from("bar");
let combined = s1 + &s2;
println!("Stitching Results: {}", combined);
let data = vec![String::from("a"), String::from("b")];
for word in &data {
println!("Borrow: {}", word);
}
println!("The vector is still available: {:?}", data);
let data2 = vec![1, 2, 3];
for num in data2 {
println!("Consumption: {}", num);
}
}
Resultado:
Original vector: ["hello", "rust", "world"]
The First Word: hello
Remove Index 1: Some(20), Remaining: ["hello", "world"]
Pop up: 50
Pop up: 40
Pop up: 30
Pop up: 20
Pop up: 10
The vector is empty: []
Stitching Results: foobar
Borrow: a
Borrow: b
The vector is still available: ["a", "b"]
Consumption: 1
Consumption: 2
Consumption: 3
for item in &vecTraversal de empréstimo (o vetor permanece disponível),for item in vecTraversal de consumo (o vetor é movido).+O operador consome a String à esquerda.pop()RetornaOption<T>e consome o último elemento.
❓ Perguntas Frequentes
P: Por que o conceito de propriedade foi projetado no Rust? Não seria mais simples usar a coleta de lixo? R: A propriedade permite que o Rust garanta a segurança da memória sem pausas da coleta de lixo. A coleta de lixo causa pausas em tempo de execução e sobrecarga adicional de memória, o que pode ser fatal para a programação de sistemas (como sistemas embarcados e motores de jogos). A propriedade transfere a tarefa de “verificar a segurança da memória” para o tempo de compilação.
P: O que acontece com a variável original após um
move? R: A variável original é marcada como “inválida” pelo compilador. Ela ainda existe na pilha, mas o compilador impede que você acesse-a novamente. Trata-se de uma verificação em tempo de compilação, não de um comportamento em tempo de execução — sem nenhuma sobrecarga em tempo de execução.
P: Quais tipos são copiáveis? R: Todos os tipos escalares (inteiros, números de ponto flutuante, booleanos, caracteres) e tuplas/matrizes compostas por eles. Qualquer tipo que exija gerenciamento de memória no heap (String, Vec, Box) não é copiável.
P: Qual tem melhor desempenho,
.clone()oumove? R:movenão tem sobrecarga, enquantoclonerequer a alocação de memória na pilha. Usemovesempre que possível e usecloneapenas quando for absolutamente necessário preservar a variável original. O compilador também otimizará e eliminará operaçõesclonedesnecessárias (mas é melhor controlar isso manualmente).
P: Como funciona a propriedade em estruturas? R: As regras de propriedade para uma estrutura como um todo são as mesmas que para uma única variável. A propriedade dos campos de uma estrutura também segue os três princípios. Quando uma estrutura é movida, a propriedade de todos os seus campos é transferida em conjunto.
📖 Resumo
- Propriedade no Rust baseia-se em três princípios fundamentais: um único proprietário, destruição automática ao final do escopo e propriedade transferível.
- Os dados na pilha (tipos escalares) seguem a semântica de Cópia; a atribuição copia automaticamente o valor.
- Os dados no heap (String, Vec) seguem a semântica de move; a atribuição transfere a propriedade.
- Após a realocação, a variável original passa a ser inválida, e o compilador bloqueia o acesso — trata-se de uma verificação em tempo de compilação
- As chamadas de função e os valores de retorno também transferem a propriedade
.clone()Cópia profunda explícita que preserva a variável original — mas à custa do desempenho
📝 Exercícios
- Dificuldade ⭐: Escreva um programa que crie um
String, atribua-o a outra variável e, em seguida, tente imprimir a primeira variável — observe a mensagem de erro do compilador. - Dificuldade ⭐⭐: Escreva uma função
fn append_world(s: String) -> Stringque acrescente “world” ao final de uma string e a retorne. Chame-a emmaine observe como a propriedade é passada para dentro e para fora da função. - Dificuldade ⭐⭐⭐: Defina uma função
fn calculate_length(s: String) -> (String, usize)que retorne uma string e seu comprimento, transferindo a propriedade da string. Verifique se a variável original ainda pode ser usada após a função ser chamada.