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.
