Error en la implementación del patrón Observer en Rust

Publicado por Brisa
hace 3 meses

Estoy intentando implementar el patrón Observer en Rust, pero estoy teniendo problemas con los tipos de datos y el lifetime. Este es mi código:

use std::rc::Rc;
use std::cell::RefCell;

struct Observable<'a> {
    observers: Vec<Rc<RefCell<dyn Observer + 'a>>>,
}

impl<'a> Observable<'a> {
    fn new() -> Self {
        Observable {
            observers: Vec::new(),
        }
    }

    fn register(&mut self, observer: Rc<RefCell<dyn Observer + 'a>>) {
        self.observers.push(observer);
    }

    fn notify(&self) {
        for observer in &self.observers {
            observer.borrow_mut().update();
        }
    }
}

trait Observer {
    fn update(&self);
}

struct ConcreteObserver {
    data: i32,
}

impl Observer for ConcreteObserver {
    fn update(&self) {
        println!("Observer updated with data: {}", self.data);
    }
}

fn main() {
    let observable = Observable::new();
    let observer = Rc::new(RefCell::new(ConcreteObserver { data: 42 }));

    observable.register(observer.clone());
    observable.notify();
}

El código compila correctamente, pero obtengo el siguiente error en tiempo de ejecución:

thread 'main' panicked at 'already borrowed: BorrowMutError'

Sospecho que el problema está relacionado con el lifetime de los observadores en Observable. ¿Cómo puedo solucionar este error y hacer que funcione correctamente la implementación del patrón Observer en Rust? Agradezco cualquier ayuda u orientación que me puedan brindar.

Rust Observer pattern error
Respuesta de Gordon Shumway
hace 3 meses

¡Hola Brisa!

El problema que estás enfrentando se debe a que en el método notify estás intentando mutar los observadores (con borrow_mut) mientras todavía tienes una referencia inmutable al vector observers debido al bucle for. Esta es una violación de las reglas de préstamo de Rust, ya que no se puede tener una referencia mutable mientras hay una referencia inmutable activa.

Para solucionarlo, puedes evitar la referencia inmutable al vector observers durando el bucle. Esto se puede hacer iterando sobre clones de los Rc<RefCell<dyn Observer>>. Al clonar el Rc, solo clonas el puntero inteligente, no el observador en sí, por lo que es una operación eficiente.

Aquí hay un ejemplo de cómo podrías hacerlo:

fn notify(&self) {
    // Clona los punteros inteligentes antes de iterar
    let observers = self.observers.iter().cloned().collect::<Vec<_>>();

    // Ahora puedes iterar sin mantener una referencia al vector original.
    for observer in observers {
        observer.borrow_mut().update();
    }
}

También, en tu función main, actualmente tu variable observable tiene un error porque debe ser mutable para poder registrar observadores. Así que debes cambiarla para que sea mutable:

fn main() {
    let mut observable = Observable::new(); // Aquí agregamos 'mut'
    let observer = Rc::new(RefCell::new(ConcreteObserver { data: 42 }));

    observable.register(observer.clone());
    observable.notify();
}

Con estos cambios, tu código debería funcionar sin problemas en tiempo de ejecución. ¡Buena suerte con la implementación del patrón Observer en Rust!