Structs en Rust: agrupar datos y añadir métodos con impl

Los structs son la forma principal de agrupar datos relacionados en Rust. A diferencia de las clases de otros lenguajes, los structs de Rust no tienen herencia. Su comportamiento se define con bloques impl que añaden métodos, y se puede componer con traits. El resultado es un sistema de tipos simple, predecible y sin sorpresas en tiempo de ejecución.

Definición e instanciación

struct Usuario {
    nombre: String,
    email: String,
    activo: bool,
    intentos_login: u32,
}

fn main() {
    let u = Usuario {
        nombre: String::from("Ana"),
        email: String::from("[email protected]"),
        activo: true,
        intentos_login: 0,
    };

    println!("{}: {}", u.nombre, u.email);
}

Mutabilidad: toda la instancia o ningún campo

En Rust no puedes marcar campos individuales como mut. La mutabilidad se aplica a la instancia entera:

let mut u = Usuario {
    nombre: String::from("Ana"),
    email: String::from("[email protected]"),
    activo: true,
    intentos_login: 0,
};

u.email = String::from("[email protected]"); // OK: u es mut

Field init shorthand

fn crear_usuario(nombre: String, email: String) -> Usuario {
    Usuario {
        nombre,  // equivale a nombre: nombre
        email,   // equivale a email: email
        activo: true,
        intentos_login: 0,
    }
}

Struct update syntax

let u1 = crear_usuario(String::from("Ana"), String::from("[email protected]"));

let u2 = Usuario {
    email: String::from("[email protected]"),
    ..u1  // los demás campos vienen de u1
};

// Nota: u1.nombre se movió a u2; u1 ya no es completamente válido

Tuple structs

struct Color(u8, u8, u8);
struct Punto(f64, f64, f64);

let negro = Color(0, 0, 0);
let origen = Punto(0.0, 0.0, 0.0);

println!("R={}", negro.0);    // acceso por índice
println!("x={}", origen.0);

Los tuple structs son útiles para dar nombres de tipo a tuplas sin necesidad de nombrar cada campo.

Unit structs

struct Marcador; // sin campos

let m = Marcador;
// Útil para implementar traits sin datos

Métodos con impl

struct Rectangulo {
    ancho: f64,
    alto: f64,
}

impl Rectangulo {
    // Método que toma &self (referencia inmutable)
    fn area(&self) -> f64 {
        self.ancho * self.alto
    }

    // Método que toma &mut self
    fn escalar(&mut self, factor: f64) {
        self.ancho *= factor;
        self.alto  *= factor;
    }

    // Método que consume self
    fn destruir(self) -> String {
        format!("{}x{}", self.ancho, self.alto)
    }

    // Función asociada (sin self) — constructor
    fn new(ancho: f64, alto: f64) -> Self {
        Self { ancho, alto }
    }

    fn cuadrado(lado: f64) -> Self {
        Self { ancho: lado, alto: lado }
    }
}

fn main() {
    let mut r = Rectangulo::new(10.0, 5.0);
    println!("Área: {}", r.area());
    r.escalar(2.0);
    println!("Área escalada: {}", r.area());
}

Múltiples bloques impl

impl Rectangulo {
    fn perimetro(&self) -> f64 {
        2.0 * (self.ancho + self.alto)
    }
}
// Rust permite varios bloques impl para el mismo tipo

Resumen

  • Los structs agrupan campos con nombre; las tuple structs agrupan sin nombres.
  • La mutabilidad aplica a la instancia entera, no a campos individuales.
  • Los métodos se definen en bloques impl.
  • &self: lectura. &mut self: modificación. self: consumo.
  • Las funciones asociadas (sin self) actúan como constructores.

El siguiente artículo cubre las tuplas y el destructuring: cómo extraer múltiples valores de una función y descomponer structs, enums y tuplas con pattern matching.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP