我遇到的情况是,Rust让我为一个泛型类型添加一个HRTB,该泛型类型在trait对象中用作参数。但是这个HRTB使嵌套闭包无法工作。
下面是我将用来创建trait对象Box<dyn OpTrait>
的trait:
trait OpTrait {}
struct Op<T>(T);
impl<T> OpTrait for Op<T> {}
下面是带有trait对象的struct:
#[derive(Clone)]
struct Succ<'a, T>(T, &'a RefCell<Option<Box<dyn OpTrait>>>);
impl<'a, T: Clone> Succ<'a, T>
where
for<'c> T: 'c,
{
fn trace(&self) -> Self {
let b = Box::new(Op(self.0.clone()));
self.1.borrow_mut().insert(b);
Succ(self.0.clone(), self.1)
}
}
#[derive(Debug, Clone)]
struct Zero;
下面的函数可以将所有这些组合在一起:
fn nest<T: Clone, F>(f: F, t: &T) -> T
where
for<'a> F: Fn(&Succ<'a, T>) -> Succ<'a, T>,
{
let trace = RefCell::new(None);
let nested = Succ(t.clone(), &trace);
let result = f(&nested);
result.0
}
我可以这样使用:
let input = Zero;
let result0 = nest(|n| n.trace(), &input);
这很有效。
但实际上嵌套nest
调用停止工作:
let result = nest(|n| nest(|nn| nn.trace(), n), &input);
--> src/main.rs:46:37
|
46 | let result = nest(|n| nest(|nn| nn.trace(), n), &input);
| - ^^^^^^^^^^
| | |
| | `n` escapes the closure body here
| | argument requires that `'1` must outlive `'static`
| `n` is a reference that is only valid in the closure body
| has type `&Succ<'1, Zero>`
注意事项:
Rust让我在Succ
的impl
中添加for<'c> T: 'c
-这与trait对象有关,我不太确定。这个HRTB导致了嵌套闭包的问题-没有它,嵌套闭包工作正常:
// ok
let result2 = nest(|n| nest(|nn| nn.clone(), n), &input);
Playground:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a5fab1aca580e4fe630f91cfa7ac0639
编辑:有一些与'一辈子的参考成功。
当我更新该结构体以删除引用(并使用Rc来支持Clone)时:
#[derive(Clone)]
struct Succ<T>(T, Rc<dyn OpTrait>);
尽管nest
上的T
需要多一个HRTB,但这对于嵌套来说工作得很好。
1条答案
按热度按时间niknxzdl1#
请考虑以下行:
由于
nest
的定义,n
的类型为&Succ<'_, Zero>
,出于同样的原因,nn
的类型为&Succ<'_, &Succ<'_, Zero>>
(所有隐藏的生命周期都是不同的,因为您没有在Fn
绑定的参数中指定具有相同参数的生命周期。尽管这并不重要)。然后,您尝试在
nn
上调用.trace()
,这需要绑定for<'c> T: 'c
。这个绑定本质上与T: 'static
相同,要看到这一点,您可以使用'c = 'static
,它必须是有效的,因为每个可能的生存期'c
都需要您的边界。在这个调用中,来自trace
定义的T
实际上是&Succ<'_, Zero>
,然而它包含未知的生命期,因此不是'static
,并且不满足for<'c> T: 'c
的边界。result0
工作的原因是因为你有一个&Succ<'_, Zero>
,其中.trace()
中的T
是Zero
,也就是'static
,所以一切都很好。同时,在
result2
中,您没有调用.trace()
,这是之前需要'static
绑定的原因,因此这也可以工作。代码中的基本问题是
Box<dyn OpTrait>
隐式地是Box<dyn OpTrait + 'static>
,而实际上您需要的是Box<dyn OpTrait + 'lifetime_of_t>
。因此,很自然地要引入一个生命周期't
来在Box
中使用并绑定T
。也就是说,将Succ
的定义更改为:然后在需要时更新其他方法以添加
't
生存期和T: 't
边界。下面是完整的固定代码:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1fe778d04c5801203111fa5ae558fcee