b1z*_*zzu 2 generics closures rust
我正在尝试在Rust写一个小程序,但我无法让它工作.
我在一个较小的脚本中重现了错误:
fn main() {
let name = String::from("World");
let test = simple(name);
println!("Hello {}!", test())
}
fn simple<T>(a: T) -> Box<Fn() -> T> {
Box::new(move || -> T {
a
})
}
Run Code Online (Sandbox Code Playgroud)
当我编译它时,我收到此错误:
error[E0310]: the parameter type `T` may not live long enough
--> test.rs:8:9
|
7 | fn simple<T>(a: T) -> Box<Fn() -> T> {
| - help: consider adding an explicit lifetime bound `T: 'static`...
8 | / Box::new(move || -> T {
9 | | a
10 | | })
| |__________^
|
note: ...so that the type `[closure@test.rs:8:18: 10:10 a:T]` will meet its required lifetime bounds
--> test.rs:8:9
|
8 | / Box::new(move || -> T {
9 | | a
10 | | })
| |__________^
Run Code Online (Sandbox Code Playgroud)
我试图添加一个明确的生命周期绑定,T: 'static如错误所示,但我收到一个新错误:
error[E0507]: cannot move out of captured outer variable in an `Fn` closure
--> test.rs:9:13
|
7 | fn simple<T: 'static>(a: T) -> Box<Fn() -> T> {
| - captured outer variable
8 | Box::new(move || -> T {
9 | a
| ^ cannot move out of captured outer variable in an `Fn` closure
Run Code Online (Sandbox Code Playgroud)
这里有几件事情,这一切都与移动语义和闭包有轻微的尴尬有关.
首先,该simple函数需要为其T参数指定生命周期.从函数的角度来看,T可以是任何类型,这意味着它可以是一个引用,因此它需要有一个生命周期.终身省略不适用于此案例,因此您需要明确写出来.编译器建议'static,这对于hello世界来说很好.如果你有更复杂的生命周期,你需要使用一个生命周期参数; 请参阅下面的示例了解更多信息
你的关闭不能是一个Fn,因为你不能多次调用它.正如你所说的新错误所说,你的闭a包在它被调用时将它捕获的值移出闭包.这与说它是一种self取而代之的方法是一回事&self.如果函数调用是普通方法而不是特殊语法,那么它将是这样的:
trait FnOnce {
type Output
fn call(self) -> Output
}
trait Fn : FnOnce {
fn call(&self) -> Output
}
// generated type
struct MyClosure<T> {
a: T
}
impl<T> FnOnce for MyClosure<T> {
fn call(self) -> T { self.a }
}
Run Code Online (Sandbox Code Playgroud)
(这并不比这些类型的实际定义简单得多.)
因此,在短期,消耗其捕获值关闭没有实现Fn,只FnOnce.调用它会消耗关闭.还有一个,FnMut但这里没有关系.
这有另一个含义,就是当它们被移动时消耗它们.您可能已经注意到,您无法调用接受self任何特征对象的方法(Box<T>其中T是特征).要移动对象,移动它的代码需要知道要移动的对象的大小.对于未标注的特征对象,不会发生这种情况.这也适用于Box<FnOnce>.因为调用闭包会移动它(因为调用是一个方法self`),所以你不能调用闭包.
那么如何解决这个问题呢?它Box<FnOnce>有点无用.有两种选择.
如果你可以使用不稳定的Rust,你可以使用FnBox类型:它是一个替代它的FnOnce内部工作Box.它隐藏在功能门后面,因为文档警告你:"请注意,FnBox如果Box<FnOnce()>闭包可以直接使用,将来可能会弃用." 这是一个使用此解决方案的操场,并添加了生命周期参数来修复原始问题.
可能是更广泛适用的工程解决方案的替代方案是避免移出封盖.
&'static T如果您始终将静态对象放入闭包中,则可以返回引用.这样你可以根据需要多次调用闭包,并且所有调用者都可以获得对同一对象的引用.
如果对象不是静态的,您可以改为返回Rc<T>.在这种情况下,所有调用者仍然获得对同一对象的引用,并且该对象的生命周期是动态管理的,因此只要需要它就会保持活动状态.这是实现此选项的另一个游乐场.
您可以让闭包将其参数复制到每个调用者.这样可以根据需要多次调用它,每个调用者都可以获得自己的副本.不需要进一步的终身管理.如果以这种方式实现它,您仍然可以使参数变为Rc<T>a而不T是以与上面的选项相同的方式使用该函数.
| 归档时间: |
|
| 查看次数: |
600 次 |
| 最近记录: |