我天真地试图这样做:
struct Foo<'a, S: Send, T:Send> {
next_:Box<Fn<(&'a mut S,), Option<T>> + Send>,
state:S
}
impl<'a, S: Send, T: Send> Iterator<T> for Foo<'a, S, T> {
fn next(&mut self) -> Option<T> {
return self.next_.call((&mut self.state,));
}
}
要创建迭代器,我可以使用闭包轻松地发送给任务。
然而,它会产生可怕的终身不匹配错误:
<anon>:8:33: 8:48 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
<anon>:8 return self.next_.call((&mut self.state,));
^~~~~~~~~~~~~~~
<anon>:7:5: 9:6 help: consider using an explicit lifetime parameter as shown: fn next(&'a mut self) -> Option<T>
<anon>:7 fn next(&mut self) -> Option<T> {
<anon>:8 return self.next_.call((&mut self.state,));
<anon>:9 }
error: aborting due to previous error
playpen: application terminated with error code 101
我不明白这个错误。
闭包应该使用生命周期'a的参数,这是结构的生命周期。
状态由结构所有,因此它的生命周期为'a。
使用next_.call((&amp; mut self.state,))不会调用任务;它应该只在call()的持续时间内,据我所知,它应该是有效的。
所以这里的不匹配是在next()中的自我生命和'a a call}之间的生命...但我不明白为什么它不会'a。
修复上述代码的正确方法是什么?
有没有更好的方法来做到这一点?
答案 0 :(得分:3)
这需要higher-rank lifetimes,因为闭包中的生命周期不应该是类型签名的一部分:闭包只是想要{em} 任何生命周期{{1} (因为它需要使用仅保证为&'a mut S
方法的内部保留的数据来调用该函数:外部没有任何名称),而不是类型签名中外部暴露(并且可控制)的生命周期。这是不可能的,但我已经看到Niko Matsakis谈论在IRC上进行处理,并且有#18837之类的预备拉请求,所以希望很快就会出现。
要明确:代码失败,因为'a
只能使用至少生成next
的引用来调用,但next_
只能使用'a
。 },这不是&mut self.state
,除非它被声明为&mut self
(这就是编译器建议的原因)。添加此生命周期是非法的,因为它不满足特征声明的要求。
你现在可以使用旧的闭包解决这个问题(这基本上就是一个'a
特征对象),甚至还有一个标准的库类型为你做这个:Unfold
。
答案 1 :(得分:3)
这是目前Rust的特质系统的一个不幸的限制,它将很快被解除。缺少higher-kinded lifetimes。据我所知,它的实施目前正在进行中。
让我们更接近检查您的结构定义(我已删除mut
以允许使用'static
进一步推理,这不会使其不那么通用):
struct Foo<'a, S: Send, T:Send> {
next_: Box<Fn<(&'a S,), Option<T>> + Send>, // '
state: S
}
'a
生命周期参数是输入参数。这意味着它由结构的用户提供,而不是由其实现者提供。
(顺便说一句,不结构实例的生命周期 - 你不能仅使用类型参数指定这样的生命周期;它必须是self
引用的生命周期,您也无法使用,因为Iterator
特征方法没有生命周期参数。但这只是一个侧面说明,它与实际问题无关)
这意味着您的结构用户可以选择'a
任意,包括选择一些大于结构生命周期的生命周期,例如'static
。现在观察这种选择如何转换结构(只需用'static
代替'a
):
struct FooStatic<S: Send, T: Send> {
next_: Box<Fn<(&'static S,), Option<T>> + Send>, // '
state: S
}
突然,关闭只能接受'static
引用,这显然不是你的情况 - self
方法中next()
的生命周期可能更短,所以你可以&#39;只是把它传递到关闭。只有在self
生命周期确实对应'a
时(如编译器所建议),这才有效:
fn next(&'a mut self) -> Option<T>
但是,正如我之前所说,你不能写这个,因为它会违反特质合约。
使用更高的生命周期,可以在闭包本身上指定生命周期参数:
struct Foo<S: Send, T: Send> {
next_: Box<for<'a> Fn<(&'a mut S,), Option<T>> + Send>,
state: S
}
这样闭包的生命周期参数由闭包的调用者选择,在这种情况下,它是Iterator
特征的实现者(即你:)),所以它将是可以使用任何引用来调用next_
,包括对Foo
内部的引用。
答案 2 :(得分:0)
您可以使用两个通用特征对象:
struct IterClosure<T, C>(C) where C: FnMut() -> Option<T>;
impl<T, C> Iterator for IterClosure<T, C> where C: FnMut() -> Option<T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
(self.0)()
}
}
struct BoxedIterClosure<'a, T>(Box<FnMut() -> Option<T> + 'a>);
impl<'a, T> Iterator for BoxedIterClosure<'a, T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
(self.0)()
}
}
fn main() {
let mut it = (0..10).into_iter();
let ic = IterClosure(|| it.next());
println!("{}", ic.sum::<i32>());
let mut it = (0..10).into_iter();
for i in IterClosure(|| it.next()) {
println!("{}", i);
}
let mut it = (0..10).into_iter();
let ic = BoxedIterClosure(Box::new(|| it.next()));
println!("{}", ic.fold(0 as i32, |s, i| s+i));
}
您可以选择哪种实施符合更好的旅行要求。在BoxedIterClosure
生命周期'a
生命周期内,您只需借用闭包上下文而无需移动它。
我想提一下另一个SO答案,它解释了如何存储和调用回调。