teoria

Structs: Definição, Instanciação e Campos

Aprenda sobre structs: definição, instanciação e campos

25 min
Aula 1 de 5

📚 Structs: Definição, Instanciação e Campos

Bem-vindos à aula sobre structs em Rust! 🚀 Neste módulo, mergulharemos em uma das ferramentas mais poderosas da linguagem para organizar dados: as estruturas de dados personalizadas, ou structs.

As structs permitem que você crie seus próprios tipos de dados, agrupando valores relacionados em uma única unidade significativa. Pense nelas como uma forma de dar um nome a uma coleção de dados, tornando seu código mais legível e organizado.

🎯 1. Introdução: Por que precisamos de Structs?

Imagine que você está desenvolvendo um sistema para gerenciar usuários. Cada usuário tem um nome de usuário, um email, um status de atividade e uma contagem de logins. Sem structs, você poderia ter variáveis separadas como:

// Sem structs
let username = String::from("alice123");
let email = String::from("alice@exemplo.com");
let active = true;
let sign_in_count = 1;
 
let username2 = String::from("bob456");
let email2 = String::from("bob@exemplo.com");
let active2 = false;
let sign_in_count2 = 5;

Isso funciona, mas se torna rapidamente inviável e propenso a erros. Como saber que username e email estão relacionados? E se você precisar passar todos esses dados para uma função? Teria que passar múltiplos parâmetros.

As structs resolvem isso! Elas permitem que você agrupe esses dados logicamente, criando um tipo User que encapsula todas essas informações.

🧠 2. Explicação Detalhada com Exemplos

Rust oferece três tipos principais de structs:

  1. Structs com nome de campos (C-like structs): O tipo mais comum, similar a objetos em outras linguagens ou structs em C.
  2. Structs de tupla: Semelhantes a tuplas, mas com um nome de tipo.
  3. Structs unitárias: Úteis para marcar tipos ou implementar traits sem dados.

Vamos explorar cada um deles.

2.1. Definição de Structs (C-like Structs)

Para definir uma struct, usamos a palavra-chave struct seguida pelo nome da struct e, dentro de chaves {} , os campos da struct com seus respectivos tipos.

Exemplo Oficial Adaptado:

// Definição da struct User
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

Aqui, User é o nome da nossa struct. Ela possui quatro campos: username (do tipo String), email (do tipo String), sign_in_count (do tipo u64) e active (do tipo bool).

Importante sobre String vs &str: Note que usamos String para username e email. Isso porque queremos que a struct possua esses dados. Se usássemos &str, teríamos que lidar com lifetimes, o que é um tópico mais avançado. Por enquanto, para dados que a struct deve possuir e gerenciar, String é a escolha comum.

2.2. Instanciação de Structs

Para criar uma instância de uma struct, você especifica um valor concreto para cada campo. A ordem dos campos na instanciação não importa, desde que todos os campos sejam preenchidos.

fn main() {
    // 📦 Instanciando a struct User
    let user1 = User {
        email: String::from("alguem@exemplo.com"),
        username: String::from("alguem123"),
        active: true,
        sign_in_count: 1,
    };
 
    println!("Usuário: {}", user1.username);
    println!("Email: {}", user1.email);
}

Acessando Campos: Para acessar os valores dos campos de uma instância de struct, usamos a notação de ponto (.), como em user1.username.

2.3. Structs Mutáveis

Por padrão, as instâncias de struct são imutáveis, assim como outras variáveis em Rust. Para que uma instância seja mutável, você precisa usar a palavra-chave mut na declaração da variável.

fn main() {
    let mut user2 = User {
        email: String::from("outro@exemplo.com"),
        username: String::from("outro456"),
        active: true,
        sign_in_count: 1,
    };
 
    println!("Email original: {}", user2.email);
 
    // ✏️ Alterando um campo de uma struct mutável
    user2.email = String::from("novo_outro@exemplo.com");
 
    println!("Novo email: {}", user2.email);
}

Importante: Se a instância é mutável, todos os seus campos são mutáveis. Você não pode ter uma instância que seja parcialmente mutável.

2.4. Sintaxe de Atalho para Criação de Instâncias

Quando os nomes das variáveis que você está usando para criar uma instância de struct são os mesmos que os nomes dos campos da struct, você pode usar uma sintaxe de atalho.

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}
 
fn build_user_shorthand(email: String, username: String) -> User {
    // ✍️ Sintaxe de atalho: se o nome do parâmetro é igual ao nome do campo
    User {
        email,      // Equivalente a email: email,
        username,   // Equivalente a username: username,
        active: true,
        sign_in_count: 1,
    }
}

2.5. Sintaxe de Atualização de Struct (Struct Update Syntax)

É comum criar uma nova instância de struct que usa a maioria dos valores de uma instância existente, mas muda apenas alguns. Para isso, Rust oferece a sintaxe de atualização de struct, usando .. (duplo ponto).

fn main() {
    let user1 = User {
        email: String::from("alguem@exemplo.com"),
        username: String::from("alguem123"),
        active: true,
        sign_in_count: 1,
    };
 
    // 🔄 Criando user3 a partir de user1, mudando apenas o email
    let user3 = User {
        email: String::from("outro_email@exemplo.com"),
        ..user1 // Copia os valores restantes de user1
    };
 
    println!("Usuário 3 username: {}", user3.username); // Saída: "alguem123"
    println!("Usuário 3 email: {}", user3.email);     // Saída: "outro_email@exemplo.com"
    println!("Usuário 3 active: {}", user3.active);   // Saída: "true"
}

Atenção: A sintaxe ..user1 move os dados de user1 para user3. Se user1 contiver dados que não implementam o trait Copy (como String), user1 não poderá mais ser usado após a criação de user3 para os campos que foram movidos. Campos que implementam Copy (como bool e u64) são copiados, permitindo que user1 ainda seja usado para esses campos.

2.6. Structs de Tupla (Tuple Structs)

As structs de tupla são semelhantes às tuplas, mas têm um nome. Isso é útil quando você quer dar um nome a uma tupla para distingui-la de outras tuplas com os mesmos tipos, mas significados diferentes.

// 🌈 Definindo structs de tupla
struct Color(i32, i32, i32); // Representa RGB
struct Point(i32, i32, i32); // Representa coordenadas (x, y, z)
 
fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
 
    // Acessando campos de structs de tupla
    println!("Cor preta (R): {}", black.0);
    println!("Ponto de origem (X): {}", origin.0);
 
    // Embora black.0 e origin.0 tenham o mesmo valor e tipo,
    // seus tipos são diferentes (Color vs Point), evitando confusão.
    // let x = black; // Isso não compila, pois Color não é Point
}

2.7. Structs Unitárias (Unit-Like Structs)

Structs unitárias são structs que não possuem nenhum campo. Elas são úteis quando você precisa implementar um trait em algum tipo, mas não precisa de nenhum dado dentro da struct.

// 🏷️ Definindo uma struct unitária
struct AlwaysEqual;
 
fn main() {
    let subject = AlwaysEqual;
    // Não há campos para acessar ou modificar
    // Geralmente usadas com traits para adicionar comportamento
}

Um exemplo prático pode ser um "marker trait" (trait de marcação) ou para indicar um evento onde os dados em si não são relevantes, mas a ocorrência do evento sim.

📝 3. Código de Exemplo Oficial (The Rust Programming Language)

Os exemplos acima são adaptações diretas dos conceitos e exemplos encontrados no Capítulo 5 do livro oficial "The Rust Programming Language", que é a documentação mais confiável e atualizada para aprender Rust.

Você pode consultar o capítulo original aqui: The Rust Programming Language - Chapter 5: Using Structs to Structure Related Data

🤝 4. Integração (N/A)

Como este tópico aborda um conceito fundamental de Rust (structs), a ideia de "integração com outras tecnologias" não se aplica diretamente aqui. Structs são um bloco de construção básico da linguagem, usados em praticamente todas as aplicações Rust, independentemente de outras bibliotecas ou frameworks.

✍️ 5. Exercício/Desafio

Para solidificar seu entendimento sobre structs, tente o seguinte desafio:

Crie uma struct chamada Rectangle que tenha dois campos: width (largura) e height (altura), ambos do tipo u32. Em seguida, no main ou em uma função auxiliar:

  1. Defina a struct Rectangle.
  2. Instancie uma Rectangle com largura 30 e altura 50.
  3. Calcule a área do retângulo (largura * altura).
  4. Imprima a área calculada.

💡 Dica Lembre-se de como acessar os campos de uma struct instanciada!

// Seu código vai aqui!
// struct Rectangle { ... }
// fn main() { ... }

🌟 6. Resumo e Próximos Passos

Nesta aula, exploramos os fundamentos das structs em Rust:

  • Definimos como criar seus próprios tipos de dados agrupando informações relacionadas.
  • Instanciamos structs e aprendemos a acessar seus campos.
  • Vimos como tornar structs mutáveis para alterar seus dados.
  • Conhecemos a sintaxe de atalho e a sintaxe de atualização para criação de instâncias.
  • Exploramos os diferentes tipos: structs com nome de campos, structs de tupla e structs unitárias.

As structs são um pilar para a organização de dados em Rust. No entanto, elas são apenas o começo!

Próximos Passos:

Na próxima aula, vamos aprofundar ainda mais, aprendendo como adicionar comportamento às suas structs através de métodos e funções associadas. Isso permitirá que suas structs não apenas armazenem dados, mas também realizem ações relacionadas a esses dados!

Fiquem ligados! 🚀

© 2025 Escola All Dev. Todos os direitos reservados.

Structs: Definição, Instanciação e Campos - Curso gratuito de Rust: A linguagem mais amada | escola.all.dev.br