Rust: Estruturas em Rust (Struct)

Última atualização: 2026-08-26

As estruturas (Struct) são a principal ferramenta para definir tipos de dados personalizados em Rust — elas agrupam dados relacionados em um todo significativo.

Se uma variável é “um único dado”, então uma estrutura é “um conjunto de dados relacionados”. É como uma tabela — cada linha é uma instância e cada coluna é um campo.


1. O que você vai aprender


2. Diagramas conceituais

100%
flowchart LR
    subgraph "struct Definition"
        DEF["struct User"]
        F1["username: String"]
        F2["email: String"]
        F3["active: bool"]
        F4["sign_in_count: u64"]
    end
    subgraph "Instantiation"
        INS["let user = User { ... }"]
        V1["username: 'alice'"]
        V2["email: 'alice@example.com'"]
        V3["active: true"]
        V4["sign_in_count: 1"]
    end
    F1 -.->|"Field Mapping"| V1
    F2 -.->|"Field Mapping"| V2
    F3 -.->|"Field Mapping"| V3
    F4 -.->|"Field Mapping"| V4

3. A história de um sistema de gestão de alunos

(1) O Desafio: Como lidar com alunos com variáveis dispersas

Maria é uma professora que precisa gerenciar informações sobre os alunos de sua turma. Cada aluno tem:

No início, ela escreveu:

RUST
let name1 = "Xiao Ming";
let age1 = 18;
let grade1 = "Senior Year";
let gpa1 = 3.8;

let name2 = "Xiao Hong";
let age2 = 17;
let grade2 = "11th Grade";
let gpa2 = 3.5;

Quando o número de alunos chega a 40, essas variáveis dispersas tornam-se completamente incontroláveis. Para imprimir um histórico escolar, é preciso escrever 40 linhas de código, e alterar um único campo significa fazer alterações em quatro lugares diferentes.

(2) A abordagem das estruturas no Rust

RUST
struct Student {
    name: String,
    age: u8,
    grade: String,
    gpa: f64,
}

fn main() {
    let student1 = Student {
        name: String::from("Xiao Ming"),
        age: 18,
        grade: String::from("Senior 3"),
        gpa: 3.8,
    };

    let student2 = Student {
        name: String::from("Xiao Hong"),
        age: 17,
        grade: String::from("Senior 2"),
        gpa: 3.5,
    };

    println!("{}: {} years old, Grade: {}, GPA: {:.1}",
        student1.name, student1.age, student1.grade, student1.gpa);
    println!("{}: {} years old, Grade: {}, GPA: {:.1}",
        student2.name, student2.age, student2.grade, student2.gpa);
}

Uma estrutura é como um “modelo” — ela define quais informações um “aluno” deve conter. Cada instância é como um formulário preenchido. O acesso aos campos é feito por meio de ., o que é claro e seguro.


4. Tipos de estruturas

100%
graph TB
    A[struct Type] --> B[Naming Structures: Fields have names and types]
    A --> C[Tuple Structure: The field has a type but no name]
    A --> D[Unit Structure: No fields]
    B --> E[struct User { name: String, age: u8 }]
    C --> F[struct Color(i32, i32, i32)]
    D --> G[struct EmptyMarker]
Tipo Definição Casos de uso
Convenções de nomenclatura struct S { f1: T, f2: T } Na maioria dos casos, os nomes dos campos são autoexplicativos
Estrutura da tupla struct S(T1, T2) Encapsula um único conceito (por exemplo, cor RGB, coordenadas)
Estrutura da unidade struct S; Marcador de tipo, espaço reservado para a implementação da característica

(2) Métodos versus funções de associação

Tipo Primeiro parâmetro Convenção de chamada Propriedade Usos típicos
Método &self obj.method() Obter Ler dados da instância
Método &mut self obj.method() Empréstimo de variáveis Modificação de dados de instância
Método self obj.method() Consumo Converter/Destruir instância
Funções relacionadas Nenhuma Type::function() Construtores, métodos de fábrica

(3) Referência rápida para características derivadas comumente utilizadas

Característica Funcionalidade Condições automáticas Exemplo
Debug {:?} Impressão formatada Depurar todos os campos #[derive(Debug)]
Clone .clone() Cópia profunda Clonar todos os campos #[derive(Clone)]
Copy Cópia automática na atribuição Clonar + Copiar todos os campos #[derive(Copy, Clone)]
PartialEq == / != Comparação Todos os campos implementam PartialEq #[derive(PartialEq)]
Hash Usado como chave do HashMap Todos os campos implementam Hash #[derive(Hash)]
Default Valor padrão Todos os campos têm um valor padrão #[derive(Default)]

5. Exemplos de estrutura

▶ Exemplo 1: Uso completo de estruturas nomeadas (Dificuldade ⭐)

RUST
// ============================================
// Naming Structures: Definition, Instantiation, Field Access
// ============================================

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    // Create an Instance
    let rect = Rectangle {
        width: 30,
        height: 50,
    };

    // Access Fields
    println!("Width: {}, Height: {}", rect.width, rect.height);

    // Variable Instances
    let mut mutable_rect = Rectangle {
        width: 10,
        height: 20,
    };
    mutable_rect.width = 15;  // ✅ Can be modified
    println!("Width after modification: {}", mutable_rect.width);

    // Use Debug Print
    println!("rect: {:?}", rect);
    println!("rect (pretty): {:#?}", rect);
}

Resultado:

TEXT 📖 Somente leitura
Width: 30, Height: 50
Width after modification: 15
rect: Rectangle { width: 30, height: 50 }
rect (pretty): Rectangle {
    width: 30,
    height: 50,
}

#[derive(Debug)] Permite que estruturas sejam exibidas usando a formatação {:?} ou {:#?}. Esse é um dos traços derivados mais comumente usados durante a depuração.


▶ Exemplo 2: Sintaxe para atualizar estruturas e notação abreviada de campos (Dificuldade ⭐⭐)

RUST
// ============================================
// Initializing Field Abbreviations + Update Syntax
// ============================================

#[derive(Debug)]
struct User {
    username: String,
    email: String,
    active: bool,
    sign_in_count: u64,
}

fn build_user(username: String, email: String) -> User {
    User {
        username,   // When field names and variable names are the same, they can be written in shorthand.
        email,      // Equivalent to email: email
        active: true,
        sign_in_count: 1,
    }
}

fn main() {
    let user1 = build_user(
        String::from("alice"),
        String::from("alice@example.com"),
    );

    // Update Syntax: Based on user1 create user2, modify only email and username
    let user2 = User {
        email: String::from("alice_new@example.com"),
        username: String::from("alice_new"),
        ..user1  // Other fields copied from user1 (Note: String fields will move!)
    };
    // println!("user1: {}", user1.username);  // ❌ username was moved to user2

    println!("user2: {:?}", user2);
    println!("user1.active: {}", user1.active);  // ✅ bool is Copy, still valid
}

Resultado:

TEXT 📖 Somente leitura
user2: User { username: "alice_new", email: "alice_new@example.com", active: true, sign_in_count: 1 }
user1.active: true

Abreviação de campo: Quando o nome de uma variável coincide com o nome de um campo de uma estrutura, os dois pontos em field: field podem ser omitidos. A sintaxe atualizada .. copia os campos restantes de outra instância — observe que isso utiliza a semântica de movimentação, em vez de Clone.


▶ Exemplo 3: O bloco impl — Adicionando métodos a uma estrutura (Dificuldade: ⭐⭐)

RUST
// ============================================
// impl block: Methods (&self) and associated functions (no &self)
// ============================================

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // Methods: &self is a reference to a Rectangle instance
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }

    // Associative Functions (similar to static methods): no &self
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 20,
    };
    let square = Rectangle::square(15);  // Correlation functions are used to :: Call

    println!("rect1 Area: {}", rect1.area());  // 1500
    println!("rect1 Can it accommodate? rect2: {}", rect1.can_hold(&rect2));  // true
    println!("Area of a Square: {}", square.area());  // 225
}

Resultado:

TEXT 📖 Somente leitura
rect1 Area: 1500
rect1 Can it accommodate? rect2: true
Area of a Square: 225

O primeiro parâmetro de um método (Method) é &self (ou &mut self), e ele é chamado por meio de .. Uma função associada (Associated Function) não possui &self e é chamada por meio de :: — um exemplo clássico é String::from().


▶ Exemplo 4: Estruturas de tuplas (Dificuldade ⭐⭐)

RUST
// ============================================
// Tuple Structure: Suitable for "it's just a bunch of values" scenarios
// ============================================

#[derive(Debug)]
struct Color(i32, i32, i32);  // RGB Color

#[derive(Debug)]
struct Point(i32, i32, i32);  // 3D Coordinates

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);

    // Fields Accessed via Indexes (tuple-like)
    println!("Black: R={}, G={}, B={}", black.0, black.1, black.2);
    println!("The Beginning: x={}, y={}, z={}", origin.0, origin.1, origin.2);

    // Tuple structures are not the same as tuples!
    // let wrong: Color = origin;  // ❌ Compilation Error: Point cannot be assigned to Color

    // Although their internal structures are the same, they are different types
    // This prevents "mixing up colors and coordinates" bugs
}

Resultado:

TEXT 📖 Somente leitura
Black: R=0, G=0, B=0
The Beginning: x=0, y=0, z=0

Uma estrutura de tupla não possui nomes de campos, apenas tipos. É semelhante a uma tupla, mas possui seu próprio nome de tipo. Color(0,0,0) e Point(0,0,0) são idênticos internamente, mas têm tipos diferentes — isso evita confusão.


▶ Exemplo 5: Exercício abrangente — Sistema de gestão de alunos (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example: Structure-Based Approach, Associated Functions and Update Syntax
// ============================================

#[derive(Debug, Clone)]
struct Student {
    name: String,
    age: u8,
    scores: Vec<u32>,
}

impl Student {
    fn new(name: &str, age: u8) -> Self {
        Student {
            name: name.to_string(),
            age,
            scores: Vec::new(),
        }
    }

    fn add_score(&mut self, score: u32) {
        self.scores.push(score);
    }

    fn average(&self) -> f64 {
        if self.scores.is_empty() {
            return 0.0;
        }
        let sum: u32 = self.scores.iter().sum();
        sum as f64 / self.scores.len() as f64
    }

    fn grade(&self) -> char {
        match self.average() as u32 {
            90..=100 => 'A',
            80..=89  => 'B',
            70..=79  => 'C',
            60..=69  => 'D',
            _        => 'F',
        }
    }

    fn summary(&self) -> String {
        format!("{} ({} years old) Average: {:.1} Level: {}", self.name, self.age, self.average(), self.grade())
    }
}

fn main() {
    let mut alice = Student::new("Alice", 20);
    alice.add_score(95);
    alice.add_score(88);
    alice.add_score(92);
    println!("{}", alice.summary());

    let mut bob = Student::new("Bob", 21);
    bob.add_score(72);
    bob.add_score(65);
    bob.add_score(58);
    println!("{}", bob.summary());

    let mut charlie = Student {
        name: String::from("Charlie"),
        ..alice.clone()
    };
    charlie.name = String::from("Charlie");
    charlie.add_score(100);
    println!("{}", charlie.summary());

    let students = [&alice, &bob, &charlie];
    println!("\n=== Class Rankings ===");
    let mut ranked: Vec<_> = students.iter().collect();
    ranked.sort_by(|a, b| b.average().partial_cmp(&a.average()).unwrap());
    for (i, s) in ranked.iter().enumerate() {
        println!("#{}: {}", i + 1, s.summary());
    }
}

Resultado:

TEXT 📖 Somente leitura
Alice (20 years old) Average: 91.7 Level: A
Bob (21 years old) Average: 65.0 Level: D
Charlie (20 years old) Average: 93.8 Level: A

=== Class Rankings ===
#1: Charlie (20 years old) Average: 93.8 Level: A
#2: Alice (20 years old) Average: 91.7 Level: A
#3: Bob (21 years old) Average: 65.0 Level: D

Este exemplo combina o uso da função de associação new(), dos métodos add_score()/average()/grade() e das sintaxes de atualização ..alice.clone() e #[derive(Debug, Clone)]. As estruturas são as principais ferramentas para organizar dados e métodos relacionados.


❓ Perguntas Frequentes

P: Como é tratada a propriedade dos campos de uma estrutura? R: A própria estrutura segue as regras de propriedade. Se um campo da estrutura for um String, a propriedade desse campo é transferida quando a estrutura é movida. Se o campo for emprestado (uma referência), a estrutura deve ser anotada com um tempo de vida.

P: Quando se deve usar métodos (com &self) e funções associadas (sem &self)? R: Use métodos quando precisar manipular dados de instância e use funções associadas quando não precisar desses dados. Os métodos são chamados usando obj.method(), enquanto as funções associadas são chamadas usando Type::function(). As funções associadas são frequentemente usadas em “construtores” (como String::from).

P: Qual é a diferença entre &self e self? R: &self é uma referência (sem transferência de propriedade), enquanto self consome a instância (transferência de propriedade). A maioria dos métodos usa &self porque precisa apenas visualizar os dados e não precisa retê-los. self é usado em cenários em que a instância é transformada ou destruída.

P: Uma estrutura pode armazenar uma referência? R: Sim, mas é preciso especificar seu tempo de vida. Por exemplo, struct S<'a> { r: &'a str }. Sem uma especificação de tempo de vida, o compilador não sabe por quanto tempo a referência permanecerá ativa (veja a Lição 11).

P: O que #[derive(Debug)] faz? R: Ele implementa automaticamente o trait Debug para estruturas. O trait Debug permite que as estruturas sejam impressas no formato {:?}. O Rust também oferece suporte a derive outras traits, como Clone, Copy, PartialEq e assim por diante.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Defina uma estrutura Book que contenha title: String, author: String e year: u32. Crie duas instâncias e imprima o título e o autor.
  2. Dificuldade ⭐⭐: Adicione um método fn perimeter(&self) -> u32 ao Rectangle para calcular o perímetro. Adicione uma função associada fn from_width(w: u32) -> Rectangle para criar um retângulo com largura e altura iguais.
  3. Dificuldade ⭐⭐⭐: Defina uma estrutura de tupla Distance(f64, f64) para representar distâncias (em metros e centímetros). Implemente um método fn to_meters(&self) -> f64 que retorne o número total de metros (por exemplo, Distance(1, 50) retorna 1.50).
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%