Error de lifetime en función que devuelve una referencia mutable en Rust

Publicado por Brisa
hace 4 meses

Etiquetas: Rust, lifetime, referencia mutable

¡Hola a todos!

Estoy intentando implementar una función en Rust que tome una referencia mutable de un objeto y devuelva otra referencia mutable a través de un cierre. Aquí está el código:

struct MiStruct {
    valor: i32,
}

impl MiStruct {
    fn nueva() -> Self {
        MiStruct { valor: 0 }
    }

    fn mutador<F>(&mut self, f: F) -> &mut Self
    where
        F: Fn(&mut Self),
    {
        f(self);
        self
    }
}

fn main() {
    let mut mi_struct = MiStruct::nueva();
    mi_struct.mutador(|x| x.valor += 1);
}

Sin embargo, al intentar compilar el programa, obtengo el siguiente error:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:14:9
   |
13 |         f(self);
   |           ---- first mutable borrow occurs here
14 |         self
   |         ^^^^ second mutable borrow occurs here
15 |     }
   |     - returning this value requires that `*self` is borrowed for `'static`

¿Podrían explicarme qué significa este error y cómo puedo solucionarlo? Aprecio mucho su ayuda. ¡Gracias!

Respuesta de Gordon Shumway
hace 4 meses

¡Hola Brisa!

El error que estás enfrentando se debe a las reglas de préstamo de Rust, las cuales no permiten tener más de un préstamo mutable (&mut) al mismo tiempo. Cuando llamas a la función f con self como argumento, estás creando el primer préstamo mutable. Luego intentas devolver self como otro préstamo mutable, y ahí es donde Rust se queja porque aún estás dentro de la misma función y no puedes tener dos préstamos mutables simultáneos del mismo recurso.

Para solucionar esto, debes asegurarte de que el préstamo mutable que le pasaste a la función f haya terminado antes de intentar devolver otro préstamo mutable de self.

Aquí tienes una posible solución:

struct MiStruct {
    valor: i32,
}

impl MiStruct {
    fn nueva() -> Self {
        MiStruct { valor: 0 }
    }

    // Ahora la función mutador toma un cierre que opera sobre un &mut i32
    fn mutador<F>(&mut self, mut f: F) -> &mut Self
    where
        F: FnMut(&mut i32),
    {
        // Llamamos el cierre pasando el valor mutable
        f(&mut self.valor);
        // Luego devolvemos self sin problemas
        self
    }
}

fn main() {
    let mut mi_struct = MiStruct::nueva();
    mi_struct.mutador(|x| *x += 1);
}

Con estos cambios, ahora pasas exclusivamente la parte mutable que quieres que el cierre modifique (&mut self.valor), y no hay conflicto de préstamos mutables porque el bloque de f termina antes de que intentes devolver self.

De esta manera, le das a f un alcance muy concreto: sólo puede modificar valor, y una vez que ha terminado de hacerlo, el préstamo mutable concluye y puedes continuar y devolver otra referencia mutable a self. Es la magia de Rust para ayudarte a escribir código concurrente seguro de manejo de memoria.

Espero que esto resuelva tu inquietud. ¡Sigue programando!