我天真地尝试这样做:
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((&mut self.state,))不会调用任务;它应该只在call()的持续时间内,据我所知,它应该是有效的 .
所以这里的不匹配是在next()中的自我生命和'a a call in ...之间的不一致...但我不明白为什么它不会'a .
修复上面代码的正确方法是什么?
有没有更好的方法来做到这一点?
3 回答
这需要higher-rank lifetimes,因为闭包中的生命周期不应该是类型签名的一部分:闭包只是想在任何生命周期中使用
&'a mut S
'a
(因为它需要调用具有仅保证持续内部的数据的函数)next
方法:外部无法命名),而不是类型签名中外部暴露(并且可控制)的生命周期 . 这并没有看到Niko Matsakis在IRC上谈论它的工作,并且有预备拉动请求,如#18837所以这有望很快出现 .要明确:代码失败,因为
next_
只能使用至少'a
的引用来调用,但&mut self.state
只能使用&mut self
,这不是'a
,除非它被声明为&'a mut self
(这就是编译器建议的原因)它) . 添加此生命周期是非法的,因为它不满足特征声明的要求 .你现在可以使用旧的闭包来解决这个问题(这本质上就是一个
Fn
trait对象),甚至还有一个标准的库类型为你做这个:Unfold .这是目前Rust的特质系统的一个不幸的限制,它将很快被解除 . 这是缺乏higher-kinded lifetimes . 据我所知,其实施目前正在进行中 .
让's examine your struct definition closer (I' ve删除
mut
以允许使用'static
进一步推理,这不会使它不那么通用):'a
lifetime参数是此处的输入参数 . 这意味着它由结构的用户提供,而不是由其实现者提供 .(顺便说一句,它不是结构实例的生命周期 - 你不能只用类型参数指定这样的生命周期;它必须是
self
引用的生命周期,你也不能使用它,因为Iterator
trait方法没有生命周期参数 . 但这只是一个侧面说明,它与实际问题无关)这意味着您的结构的用户可以任意选择
'a
,包括选择一些大于结构生命周期的生命周期,例如'static
. 现在观察这种选择如何转换结构(只需用'static
代替'a
):突然之间,闭包只能接受
'static
引用,这显然不是你的情况 -next()
方法中self
的生命周期可能会更短,所以你不能把它传递给闭包 . 这只能在self
lifetime确实对应'a
(由编译器建议)时才有效:但是,正如我之前所说,你不能写这个,因为它会违反特质 Contract .
使用更高的生命周期,可以在闭包本身上指定生命周期参数:
这样闭包的生命周期参数由闭包的调用者选择,在这种情况下,它是
Iterator
trait的实现者(即你:)),因此可以用任何引用调用next_
,包括引用进入Foo
内部 .你可以通过使用两个通用特征对象来做到这一点:
您可以选择适合更好的旅行要求的实施 . 在
BoxedIterClosure
的情况下'a
生命周期你可以借用闭包上下文而不移动它 .我想提一下另一个解释如何存储和调用回调的答案 .