En Rust, las estructuras (structs) son contenedores de datos, pero para que sean útiles, necesitamos definir el comportamiento asociado a ellas. Esto se logra mediante bloques impl, que permiten agrupar funciones relacionadas con un tipo específico.
Existen dos tipos de funciones dentro de un bloque impl: las funciones asociadas y los métodos. Las funciones asociadas no reciben como primer parámetro a la instancia de la estructura (no llevan self); se utilizan comúnmente como constructores, como Contador::nuevo(), y se llaman mediante el operador de doble dos puntos ::. Por otro lado, los métodos son funciones que operan sobre una instancia específica y reciben self como primer parámetro.
El uso de self define la forma en que el método interactúa con la memoria y la propiedad (ownership):
1. &self (Referencia inmutable): Permite leer los datos de la instancia pero no modificarlos. Es el más común.
2. &mut self (Referencia mutable): Permite modificar los datos internos de la instancia. Requiere que la instancia original haya sido declarada con let mut.
3. self (Consumo de la instancia): El método toma la propiedad (ownership) de la instancia. Una vez llamado, la variable original deja de ser válida, lo cual es útil en patrones como el Builder o cuando una transformación consume el objeto original.
Si intentas modificar un campo usando &self, el compilador de Rust te detendrá con un error de mutabilidad. Si intentas usar una variable después de haber llamado a un método con self, recibirás un error de “valor movido” (use of moved value), garantizando la seguridad de la memoria.
struct Contador {
valor: u32,
}
impl Contador {
// Función asociada (Constructor)
// No recibe `self`, se llama como Contador::nuevo(5)
fn nuevo(inicial: u32) -> Self {
Self { valor: inicial }
}
// Método con referencia inmutable (&self)
// Permite leer el campo `valor` sin modificarlo
fn leer_valor(&self) -> u32 {
self.valor
}
// Método con referencia mutable (&mut self)
// Permite modificar el campo `valor`
fn incrementar(&mut self) {
self.valor += 1;
}
// Método que consume la instancia (self)
// Toma la propiedad de `self`, invalidando la variable original
fn consumir_y_obtener(self) -> u32 {
println!("El contador ha sido consumido.");
self.valor
}
}
fn main() {
// Llamada a función asociada usando ::
let mut mi_contador = Contador::nuevo(10);
// Llamada a método inmutable usando punto (.)
println!("Valor inicial: {}", mi_contador.leer_valor());
// Llamada a método mutable usando punto (.)
// Rust ajusta automáticamente la llamada para pasar la referencia mutable
mi_contador.incrementar();
println!("Valor tras incrementar: {}", mi_contador.leer_valor());
// Llamada a método que consume la instancia
// El ownership de `mi_contador` se mueve a `consumir_y_obtener`
let valor_final = mi_contador.consumir_y_obtener();
println!("Valor final extraído: {}", valor_final);
// Error de compilación: `mi_contador` ya no es válido porque fue consumido
// mi_contador.incrementar();
}
Explicación del Código
– struct Contador: Define la estructura con un campo llamado valor de tipo u32.
– impl Contador: Abre el bloque para definir las implementaciones de Contador.
– fn nuevo(inicial: u32) -> Self: Es una función asociada. Usa Self (con S mayúscula) como un alias del tipo Contador. Se invoca con Contador::nuevo(10).
– fn leer_valor(&self): El parámetro &self indica que el método solo necesita leer los datos. Permite llamar a mi_contador.leer_valor() incluso si mi_contador no fuera mutable.
– fn incrementar(&mut self): El parámetro &mut self permite alterar el estado interno de mi_contador. Por esto, la instancia debe definirse con let mut.
– fn consumir_y_obtener(self): Al usar self sin el símbolo &, el método toma la propiedad total de la instancia. En main, tras llamar a este método, mi_contador queda “desplazado” (moved) y ya no se puede usar.
– mi_contador.incrementar(): Aunque definimos el método con &mut self, lo llamamos usando la notación de punto .. Rust aplica un “auto-referenciamiento” que transforma la llamada en Contador::incrementar(&mut mi_contador).
N° 37