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


2. Diagramas conceituais

100%
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:

“Se ao menos um livro pudesse ser emprestado por apenas uma pessoa por vez...”

(2) Regras de propriedade do Rust

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

100%
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 implementar Copy, 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.

RUST
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 ⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
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. String não implementa Copy; 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 ⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
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: ⭐⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
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 ⭐⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
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_score A 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 ⭐⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
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 &vec Traversal de empréstimo (o vetor permanece disponível), for item in vec Traversal de consumo (o vetor é movido). + O operador consome a String à esquerda. pop() Retorna Option<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() ou move? R: move não tem sobrecarga, enquanto clone requer a alocação de memória na pilha. Use move sempre que possível e use clone apenas quando for absolutamente necessário preservar a variável original. O compilador também otimizará e eliminará operações clone desnecessá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


📝 Exercícios

  1. 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.
  2. Dificuldade ⭐⭐: Escreva uma função fn append_world(s: String) -> String que acrescente “world” ao final de uma string e a retorne. Chame-a em main e observe como a propriedade é passada para dentro e para fora da função.
  3. 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.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%