MST

星途 面试题库

面试题:Rust 中基于结构体和 trait 实现观察者模式的要点

在 Rust 里实现观察者模式时,涉及到结构体之间的引用关系以及生命周期管理,请详细阐述在设计数据结构和方法时需要注意哪些要点,以避免生命周期相关的编译错误,并给出一个简洁的示例代码展示核心实现逻辑。
37.9万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

设计要点

  1. 明确生命周期参数:在涉及结构体间引用关系时,为结构体和方法显式声明生命周期参数,确保编译器能清晰知晓引用的生命周期范围。例如,如果一个结构体持有对另一个结构体的引用,要在结构体定义中体现这个引用的生命周期。
  2. 确保引用生命周期足够长:被引用的数据的生命周期必须长于或等于引用它的数据的生命周期。比如,若一个局部变量作为引用被传递到一个结构体实例中,这个局部变量的生命周期不能短于该结构体实例,否则会出现悬空引用。
  3. 避免循环引用:循环引用会导致难以确定生命周期,可能使编译器无法正确分析引用关系,从而引发编译错误。尽量设计数据结构以避免结构体间相互持有强引用形成循环。

示例代码

// 定义观察者 trait
trait Observer {
    fn update(&mut self, data: i32);
}

// 定义被观察对象
struct Subject {
    data: i32,
    observers: Vec<Box<dyn Observer>>,
}

impl Subject {
    fn new() -> Self {
        Subject {
            data: 0,
            observers: Vec::new(),
        }
    }

    fn attach(&mut self, observer: Box<dyn Observer>) {
        self.observers.push(observer);
    }

    fn set_data(&mut self, new_data: i32) {
        self.data = new_data;
        self.notify();
    }

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

// 实现一个具体的观察者
struct ConcreteObserver {
    name: String,
}

impl Observer for ConcreteObserver {
    fn update(&mut self, data: i32) {
        println!("{} observed new data: {}", self.name, data);
    }
}

你可以这样使用:

fn main() {
    let mut subject = Subject::new();

    let mut observer1 = ConcreteObserver {
        name: "Observer1".to_string(),
    };
    let mut observer2 = ConcreteObserver {
        name: "Observer2".to_string(),
    };

    subject.attach(Box::new(observer1));
    subject.attach(Box::new(observer2));

    subject.set_data(42);
}

在上述代码中,Subject 结构体持有 Observer 特征对象的 Vec,避免了循环引用问题。在 attach 方法中添加观察者时,生命周期通过 Box 来管理,确保了引用关系的正确性,避免了生命周期相关的编译错误。