为何向Rc<RefCell<dyn Observer<T>>>克隆时F和U需为'static?
问题:Rust中
Ref::computed方法的'static生命周期约束疑问 以下是我的Rust代码,在Ref::computed方法中出现错误,提示F和U必须为'static:
use std::cell::RefCell; use std::rc::Rc; trait Observer<T> { fn update(&mut self, v: &T); } struct Computed<U, F> { value: U, callback: F, } impl<T, U, F> Observer<T> for Computed<U, F> where F: Fn(&T) -> U { fn update(&mut self, v: &T) {} } struct Ref<T> { value: T, observers: Vec<Rc<RefCell<dyn Observer<T>>>>, } impl<T> Ref<T> { fn computed<U, F>(&mut self, callback: F) -> Rc<RefCell<Computed<U, F>>> where F: Fn(&T) -> U, { let s = Rc::new(RefCell::new(Computed { value: callback(&self.value), callback, })); self.observers.push(s.clone()); s } }
错误信息:
error[E0310]: the parameter type `F` may not live long enough --> src/lib.rs:34:29 | 34 | self.observers.push(s.clone()); | ^^^^^^^^^ | | | the parameter type `F` must be valid for the static lifetime... | ...so that the type `F` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | 28 | F: Fn(&T) -> U + 'static, | +++++++++ error[E0310]: the parameter type `U` may not live long enough --> src/lib.rs:34:29 | 34 | self.observers.push(s.clone()); | ^^^^^^^^^ | | | the parameter type `U` must be valid for the static lifetime... | ...so that the type `U` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | 26 | fn computed<U: 'static, F>(&mut self, callback: F) -> Rc<RefCell<Computed<U, F>>> | +++++++++
按照错误提示添加生命周期约束后代码可正常运行,但无法理解原因:既然智能指针拥有F的所有权,为何还需要声明该生命周期,且必须是'static?
解答
核心原因在于你定义的observers类型是Vec<Rc<RefCell<dyn Observer<T>>>>——这里的trait object(dyn Observer<T>)默认携带隐式的'static生命周期约束。
当你把Rc<RefCell<Computed<U,F>>>强转为Rc<RefCell<dyn Observer<T>>>时,Rust会检查Computed<U,F>是否满足trait object的生命周期要求:
Computed<U,F>内部持有U和F,这两个类型的生命周期必须覆盖trait object的生命周期。- 因为未指定生命周期的trait object默认要求
'static(即能存活到程序结束),所以U和F必须也满足'static——不能包含任何指向非静态内存的引用,否则trait object存活期间,引用可能失效,引发悬垂引用问题。
你可能疑惑“智能指针已经拥有所有权了为什么还要约束”?其实这里的'static不是针对智能指针本身的生命周期,而是指U和F这两个类型不能包含带有非静态生命周期的引用。比如如果F是一个捕获了局部变量引用的闭包,那它就不满足'static,哪怕被Rc持有,一旦局部变量销毁,闭包里的引用就会变成无效引用,这是Rust严格禁止的。
如果不想强制'static,可以给trait添加显式的生命周期参数,让U和F的生命周期与Ref实例绑定,修改代码如下:
use std::cell::RefCell; use std::rc::Rc; // 给Observer trait添加生命周期参数 trait Observer<'a, T> { fn update(&mut self, v: &T); } struct Computed<U, F> { value: U, callback: F, } // 实现时绑定对应生命周期 impl<'a, T, U, F> Observer<'a, T> for Computed<U, F> where F: Fn(&T) -> U { fn update(&mut self, v: &T) {} } struct Ref<T> { value: T, // observers绑定生命周期通配符,适配实例的生命周期 observers: Vec<Rc<RefCell<dyn Observer<'_, T>>>>, } impl<T> Ref<T> { // 绑定生命周期参数,让U和F只需与self存活周期一致 fn computed<'a, U, F>(&'a mut self, callback: F) -> Rc<RefCell<Computed<U, F>>> where F: Fn(&T) -> U + 'a, U: 'a, { let s = Rc::new(RefCell::new(Computed { value: callback(&self.value), callback, })); self.observers.push(s.clone()); s } }
这样修改后,U和F不需要强制'static,只需和Ref实例的存活周期保持一致即可,不过这也意味着computed返回的Rc不能比Ref实例存活更久。
内容的提问来源于stack exchange,提问作者Atarsei
相关产品推荐
相关产品推荐

