Menu
Coddy logo textTech

Structs Genéricas

Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 24 de 61.

Até agora, cada struct que você criou teve campos com tipos específicos e concretos. Um Pet tem um nome String, um Rectangle tem dimensões f64. Mas e se você quiser criar uma struct que possa conter qualquer tipo de dado?

É aqui que entram os generics. Os generics permitem que você escreva código flexível e reutilizável que funciona com múltiplos tipos. Em vez de definir rigidamente um tipo específico, você usa um marcador de posição — normalmente T — que é substituído por um tipo concreto quando você usa a struct.

Aqui está uma struct wrapper simples que pode conter qualquer tipo:

struct Wrapper<T> {
    value: T,
}

O <T> após o nome da struct declara um parâmetro de tipo genérico. Dentro da struct, T atua como um substituto para qualquer tipo que você realmente usará. Quando você cria uma instância, o Rust infere o tipo concreto:

let int_wrapper = Wrapper { value: 42 };        // T é i32
let float_wrapper = Wrapper { value: 3.14 };    // T é f64
let text_wrapper = Wrapper { value: "hello" };  // T é &str

Cada um destes é um tipo concreto diferente—Wrapper<i32>, Wrapper<f64> e Wrapper<&str>—mas todos compartilham a mesma definição de struct. Isso elimina a necessidade de escrever structs IntWrapper, FloatWrapper e StringWrapper separadas que fazem essencialmente a mesma coisa.

A letra T é apenas uma convenção (abreviação de "Type"). Você poderia usar qualquer identificador válido, mas T é o padrão em Rust e na maioria das outras linguagens com genéricos.

challenge icon

Desafio

Fácil

Vamos construir um contêiner genérico que pode conter qualquer tipo de valor! Você criará uma struct Container que demonstra como os genéricos tornam seu código flexível e reutilizável.

Você organizará seu código em dois arquivos:

  • container.rs: Defina uma struct genérica pública chamada Container<T> com um único campo público item do tipo T. Este contêiner deve ser capaz de conter qualquer tipo—inteiros, floats, strings ou qualquer outra coisa.
  • main.rs: Importe seu módulo de contêiner e demonstre sua flexibilidade criando contêineres que contêm diferentes tipos de dados. Você criará três contêineres e exibirá o que cada um contém.

No seu arquivo principal, crie os seguintes contêineres:

  1. Um contêiner contendo um valor inteiro (a primeira entrada, analisada como i32)
  2. Um contêiner contendo um valor de ponto flutuante (a segunda entrada, analisada como f64)
  3. Um contêiner contendo uma string (a terceira entrada, mantida como uma String)

Sua saída deve exibir o conteúdo de cada contêiner neste formato:

Integer container: {value}
Float container: {value}
String container: {value}

Por exemplo, com as entradas 42, 3.14 e hello:

Integer container: 42
Float container: 3.14
String container: hello

Observe como a mesma definição de Container<T> funciona para todos os três tipos diferentes—esse é o poder dos genéricos!

Você receberá três entradas: um valor inteiro, um valor de ponto flutuante e um valor de string.

Folha de consulta

Generics permitem que você crie structs que podem conter qualquer tipo de dado usando um marcador de posição de parâmetro de tipo.

Para declarar uma struct genérica, use colchetes angulares com um parâmetro de tipo (convencionalmente T):

struct Wrapper<T> {
    value: T,
}

O <T> declara um parâmetro de tipo genérico, e T dentro da struct atua como um marcador de posição para o tipo real.

Ao criar instâncias, o Rust infere o tipo concreto:

let int_wrapper = Wrapper { value: 42 };        // T é i32
let float_wrapper = Wrapper { value: 3.14 };    // T é f64
let text_wrapper = Wrapper { value: "hello" };  // T é &str

Cada instância se torna um tipo concreto diferente (Wrapper<i32>, Wrapper<f64>, Wrapper<&str>) enquanto compartilha a mesma definição de struct.

Experimente você mesmo

mod container;

use container::Container;

fn main() {
    // Leia as três entradas
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let int_value: i32 = input1.trim().parse().expect("Failed to parse integer");

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let float_value: f64 = input2.trim().parse().expect("Failed to parse float");

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let string_value = input3.trim().to_string();

    // TODO: Crie três containers usando Container<T>
    // 1. Um container de inteiro contendo int_value
    // 2. Um container de float contendo float_value
    // 3. Um container de string contendo string_value

    // TODO: Imprima o conteúdo de cada container no formato exigido
    // Integer container: {value}
    // Float container: {value}
    // String container: {value}
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming