Menu
Coddy logo textTech

Estructuras genéricas

Parte de la sección Object Oriented Programming del Journey de Rust de Coddy — lección 24 de 61.

Hasta ahora, cada struct que has creado ha tenido campos con tipos específicos y concretos. Un Pet tiene un nombre String, un Rectangle tiene dimensiones f64. ¿Pero qué pasa si quieres crear un struct que pueda contener cualquier tipo de datos?

Aquí es donde entran en juego los genéricos. Los genéricos te permiten escribir código flexible y reutilizable que funciona con múltiples tipos. En lugar de codificar de forma rígida un tipo específico, utilizas un marcador de posición —normalmente T— que se sustituye por un tipo concreto cuando utilizas la struct.

Aquí tienes una estructura wrapper simple que puede contener cualquier tipo:

struct Wrapper<T> {
    value: T,
}

El <T> después del nombre de la estructura declara un parámetro de tipo genérico. Dentro de la estructura, T actúa como un sustituto para cualquier tipo que realmente utilices. Cuando creas una instancia, Rust infiere el tipo concreto:

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

Cada uno de estos es un tipo concreto diferente—Wrapper<i32>, Wrapper<f64> y Wrapper<&str>—pero todos comparten la misma definición de struct. Esto elimina la necesidad de escribir structs IntWrapper, FloatWrapper y StringWrapper por separado que hacen esencialmente lo mismo.

La letra T es solo una convención (abreviatura de "Type"). Podrías usar cualquier identificador válido, pero T es el estándar en Rust y en la mayoría de los demás lenguajes con genéricos.

challenge icon

Desafío

Fácil

¡Vamos a construir un contenedor genérico que pueda albergar cualquier tipo de valor! Crearás una estructura Container que demuestra cómo los genéricos hacen que tu código sea flexible y reutilizable.

Organizarás tu código en dos archivos:

  • container.rs: Define una estructura genérica pública llamada Container<T> con un único campo público item de tipo T. Este contenedor debe ser capaz de albergar cualquier tipo: enteros, flotantes, cadenas o cualquier otra cosa.
  • main.rs: Importa tu módulo de contenedor y demuestra su flexibilidad creando contenedores que alberguen diferentes tipos de datos. Crearás tres contenedores y mostrarás lo que contiene cada uno.

En tu archivo principal, crea los siguientes contenedores:

  1. Un contenedor que albergue un valor entero (la primera entrada, analizada como i32)
  2. Un contenedor que albergue un valor flotante (la segunda entrada, analizada como f64)
  3. Un contenedor que albergue una cadena (la tercera entrada, mantenida como un String)

Tu salida debe mostrar el contenido de cada contenedor en este formato:

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

Por ejemplo, con las entradas 42, 3.14, y hello:

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

Observa cómo la misma definición de Container<T> funciona para los tres tipos diferentes: ¡ese es el poder de los genéricos!

Recibirás tres entradas: un valor entero, un valor flotante y un valor de cadena.

Hoja de referencia

Los genéricos te permiten crear estructuras que pueden contener cualquier tipo de datos utilizando un marcador de posición de parámetro de tipo.

Para declarar una estructura genérica, utiliza corchetes angulares con un parámetro de tipo (convencionalmente T):

struct Wrapper<T> {
    value: T,
}

El <T> declara un parámetro de tipo genérico, y T dentro de la estructura actúa como un marcador de posición para el tipo real.

Al crear instancias, Rust infiere el tipo concreto:

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

Cada instancia se convierte en un tipo concreto diferente (Wrapper<i32>, Wrapper<f64>, Wrapper<&str>) mientras comparten la misma definición de estructura.

Pruébalo tú mismo

mod container;

use container::Container;

fn main() {
    // Lee las tres 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: Create three containers using Container<T>
    // 1. Un contenedor de enteros que contenga int_value
    // 2. Un contenedor de flotantes que contenga float_value
    // 3. Un contenedor de cadenas que contenga string_value

    // TODO: Print cada contenido del contenedor en el formato requerido
    // Integer container: {value}
    // Float container: {value}
    // String container: {value}
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming