You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust结构体方法为何仍持有可变引用?Observer模式实现疑问

Rust Observer模式编译错误分析与解决方案

核心问题本质

你遇到的编译错误完全源于Rust的借用检查器约束:当发布者结构体持有订阅者状态的引用时,若用同一个生命周期参数绑定发布者和引用,编译器会强制要求该引用的存活期覆盖发布者的整个生命周期。这就导致connect调用后,订阅者的借用不会随函数调用结束而释放,后续对发布者或订阅者的任何操作都会触发借用冲突。

为什么可变引用无法释放?

你用同一生命周期'a绑定Entity<'a>和内部引用时,相当于告诉编译器:这个引用必须和Entity活得一样久。因此connect里的可变借用会被延长到Entity的整个生命周期,而非函数调用结束就失效——这直接违反了Rust“同一时间只能有一个可变引用,或多个不可变引用”的规则。

无需回调的可行解决方案

方案1:使用内部可变性(单线程场景)

用RefCell绕开编译期借用检查,将状态放在内部可变性容器中,发布者持有容器的不可变引用,运行时动态检查借用规则:

use std::cell::RefCell;

struct Subscriber {
    state: RefCell<Vec<i32>>,
}

struct Publisher<'a> {
    subscribers: Vec<&'a RefCell<Vec<i32>>>,
}

impl<'a> Publisher<'a> {
    fn new() -> Self {
        Publisher { subscribers: Vec::new() }
    }

    fn connect(&mut self, sub: &'a Subscriber) {
        self.subscribers.push(&sub.state);
    }

    fn publish(&self, value: i32) {
        for state in &self.subscribers {
            state.borrow_mut().push(value);
        }
    }
}

fn main() {
    let sub = Subscriber { state: RefCell::new(Vec::new()) };
    let mut pub_ = Publisher::new();
    
    pub_.connect(&sub);
    pub_.publish(42);
    
    println!("{:?}", sub.state.borrow());
}

这里发布者持有的是RefCell<Vec<i32>>的不可变引用,内部可变性允许我们在拥有不可变引用的前提下修改状态,connect调用后发布者的可变借用会正常释放,后续操作不再触发冲突。

方案2:共享所有权(多线程/多订阅场景)

用Arc+RefCell组合共享状态所有权,彻底摆脱生命周期绑定的限制:

use std::cell::RefCell;
use std::sync::Arc;

struct Subscriber {
    state: Arc<RefCell<Vec<i32>>>,
}

struct Publisher {
    subscribers: Vec<Arc<RefCell<Vec<i32>>>>,
}

impl Publisher {
    fn new() -> Self {
        Publisher { subscribers: Vec::new() }
    }

    fn connect(&mut self, sub: &Subscriber) {
        self.subscribers.push(sub.state.clone());
    }

    fn publish(&self, value: i32) {
        for state in &self.subscribers {
            state.borrow_mut().push(value);
        }
    }
}

fn main() {
    let sub = Subscriber { state: Arc::new(RefCell::new(Vec::new())) };
    let mut pub_ = Publisher::new();
    
    pub_.connect(&sub);
    pub_.publish(42);
    
    println!("{:?}", sub.state.borrow());
}

Arc会维护状态的引用计数,发布者和订阅者都持有状态的共享所有权,无需生命周期参数即可安全实现Observer模式的核心逻辑。

为何基于Vec的简化代码能正常运行?

你之前的Vec示例中,引用的生命周期仅局限于函数调用内部,没有和结构体的生命周期绑定。借用检查器允许这种临时借用,函数调用结束后借用立即释放,不会产生长期的借用冲突。

内容的提问来源于stack exchange,提问作者BlueOyster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 11:21:59