特征`std :: ops :: Fn <()>`没有为[closure ...]实现,只是来自外部作用域的绑定

mar*_*hon 3 rust

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());
}
Run Code Online (Sandbox Code Playgroud)

给出了错误

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
Run Code Online (Sandbox Code Playgroud)

Trait std :: ops :: Fn状态的文档:

Fn由闭包自动实现,闭包只对获取的变量进行不可变引用,或者根本不捕获任何内容,

s 是不可变的,但它不是一个参考,我正在移动它.

如果我调用s.clone()编译器错误消失了,但在我的实际情况下我想避免这种情况.

如果我使用FnMut FnOnce抱怨不知道大小,即使它是盒装,也会出现同样的错误.

有没有办法让这项工作与移动的数据一起工作?

操场

Fra*_*gné 5

如果这是允许的,第二次调用闭包会发生什么?请记住,第一次调用闭包时,它会移动s,所以s现在没有有效值.

根据您的需要,有几种方法可以完成这项工作.

  1. 使闭包返回对字符串的引用.

    注意:我们需要'aas强制转换表达式的右侧显式写出,否则编译器会给出错误.如果不引入中间函数(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());
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用 FnOnce FnBox而不是Fn.FnOnce闭包可以移动,但最多可以调用一次.由于我们无法调用Box<FnOnce()>Rust 1.23.0,我们需要使用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());
    }
    
    Run Code Online (Sandbox Code Playgroud)

但是,由于FnBox不稳定,您只能在夜间编译器中使用它.如果你想支持稳定的编译器,你可以使用crate中BoxFnOnce定义的类型(尽管你需要显式地调用闭包; 不起作用).boxfnoncex.call()x()