带回调的Trait:如何对参数引用生命周期建模?
泛化Holder元组的FnOnce回调Trait实现
先看一个包含FnOnce回调的Trait示例(FnMut或Fn的情况类似):
use std::cell::RefCell; use std::rc::Rc; struct Holder<T>(Rc<RefCell<T>>); trait Callback<A> { fn callback(&self, f: impl FnOnce(&A)); } impl<A> Callback<A> for Holder<A> { fn callback(&self, f: impl FnOnce(&A)) { f(&self.0.borrow()) } }
该示例支持如下调用:
let holder_a = Holder(Rc::new(RefCell::new(42))); holder_a.callback(|a| println!("{a}"));
我的目标是将该模式泛化到Holder元组,实现如下调用:
let holder_a = Holder(Rc::new(RefCell::new(42))); let holder_b = Holder(Rc::new(RefCell::new(1.0))); (holder_a, holder_b).callback(|(a, b)| println!("{a} {b}"));
并扩展到更大的元组(比如最多12个)。核心挑战是:对于(Holder<A>, Holder<B>, Holder<C>)这类元组,要为回调提供(&A, &B, &C)实例(不依赖Copy或Clone),本质是如何为这些&元素的生命周期建模?
尝试1:直接传入引用类型作为泛型参数
将Trait的泛型参数改为直接传入引用类型:
trait Callback<A> { fn callback(&self, f: impl FnOnce(A)); } impl<A> Callback<&A> for Holder<A> { fn callback(&self, f: impl FnOnce(&A)) { f(&self.0.borrow()) } } impl<A, B> Callback<(&A, &B)> for (Holder<A>, Holder<B>) { fn callback(&self, f: impl FnOnce((&A, &B))) { f((&self.0.0.borrow(), &self.1.0.borrow())) } }
签名看似正确,但编译器报错:impl has extra requirement for<'a> impl FnOnce(&A): FnOnce(&'a A),原因是Trait本身未关联FnOnce的生命周期,但实现中却引入了。
尝试2:引入生命周期泛型
引入生命周期泛型:
trait Callback<'a, Args> { fn callback(&'a self, f: impl FnOnce(Args)); } impl<'a, A> Callback<'a, &'a A> for Holder<A> { fn callback(&'a self, f: impl FnOnce(&'a A)) { f(&self.0.borrow()) } } impl<'a, A, B> Callback<'a, (&'a A, &'a B)> for (Holder<A>, Holder<B>) { fn callback(&'a self, f: impl FnOnce((&'a A, &'a B))) { f((&self.0.0.borrow(), &self.1.0.borrow())) } }
Trait与实现的关联正常,目标调用也能编译,但编译器指出借用生命周期不足:creates a temporary value which is freed while still in use,因为'a对应&'a self的生命周期,而内部借用更短。
核心疑问:如何正确为(&'a A, &'a B, ...)这类生命周期建模?是否有利用GAT(关联类型泛型)或HRTB(高阶生命周期绑定)的巧妙方法?
内容的提问来源于stack exchange,提问作者bluenote10
相关产品推荐
相关产品推荐

