fn main() {
let s = Some("xyz".to_string()); //compiler error
let foo = Box::new(|| s) as Box<Fn() -> Option<String>>; //ok
let bar = Box::new(|| Some("xyz".to_string())) as Box<Fn() -> Option<String>>;
println!("{:?}", foo());
println!("{:?}", bar());
}
给出错误
error[E0277]: the trait bound `[closure@src/main.rs:5:24: 5:28 s:std::option::Option<std::string::String>]: std::ops::Fn<()>` is not satisfied
--> src/main.rs:5:15
|
5 | let foo = Box::new(|| s) as Box<Fn() -> Option<String>>;
| ^^^^^^^^^^^^^^ the trait `std::ops::Fn<()>` is not implemented for `[closure@src/main.rs:5:24: 5:28 s:std::option::Option<std::string::String>]`
|
= note: required for the cast to the object type `std::ops::Fn() -> std::option::Option<std::string::String>`
error: aborting due to previous error
Trait std::ops::Fn州的文档:
Fn由闭包自动实现,闭包只对获取的变量进行不可变引用,或者根本不捕获任何内容,
s
不可变,但它不是参考,我正在移动它。
如果我调用s.clone()
编译器错误就会消失,但在我的实际情况中,我想避免这种情况。
如果我使用FnMut
FnOnce
抱怨不知道尺寸,即使它是盒装的,也会出现同样的错误。
有没有办法让这项工作与移动数据一起使用?
答案 0 :(得分:5)
如果允许这样做,第二次调用闭包会发生什么?请记住,第一次调用闭包时,它会移动s
,因此s
现在没有有效值。
根据您的需要,有几种方法可以完成这项工作。
使闭包返回对字符串的引用。
注意:我们需要在'a
强制转换表达式的右侧显式写出as
,否则编译器会给出错误。我不认为我们可以在不引入中间函数的情况下编写正确的生命周期(make_foo
)。
fn make_foo<'a>(s: &'a Option<String>) -> Box<Fn() -> Option<&'a str> + 'a> {
Box::new(move || s.as_ref().map(|s| &**s)) as Box<Fn() -> Option<&'a str> + 'a>
}
fn main() {
let s = Some("xyz".to_string());
let foo = make_foo(&s);
println!("{:?}", foo());
}
使用 FnOnce
FnBox
代替Fn
。 FnOnce
闭包可以移动,但最多可以调用一次。由于我们无法在Rust 1.23.0中调用Box<FnOnce()>
,因此我们需要使用Box<FnBox()>
。
#![feature(fnbox)]
use std::boxed::FnBox;
fn main() {
let s = Some("xyz".to_string());
let foo = Box::new(|| s) as Box<FnBox() -> Option<String>>;
println!("{:?}", foo());
}
但是,由于FnBox
不稳定,因此您只能将其与夜间编译器一起使用。如果你想支持稳定的编译器,你可以使用boxfnonce
crate中定义的BoxFnOnce
类型(虽然你需要明确地将闭包调用为x.call()
; x()
赢了&# 39;工作)。