在Rust中调用存储在Struct中的堆栈分配的闭包

cde*_*win 4 struct closures move rust

我在这样的结构中存储一个闭包:

#[derive(Clone)]
struct S<'a> {
    func: &'a FnOnce() -> u32
}

fn main() {
    let s = S { func: &|| 0 };
    let val = (s.func)();
    println!("{}", val);
}
Run Code Online (Sandbox Code Playgroud)

当我编译时,s.func无法移动来执行自己.我理解为什么它不能移动(即它只是一个引用,并且它的大小在编译时是不知道的),但不知道为什么它被移动 - 是否只是因为闭包是通过特征实现的?

这是错误消息:

error[E0161]: cannot move a value of type std::ops::FnOnce() -> u32:
the size of std::ops::FnOnce() -> u32 cannot be statically determined
 --> main.rs:8:15
  |
8 |     let val = (s.func)();
  |               ^^^^^^^^

error[E0507]: cannot move out of borrowed content
 --> main.rs:8:15
  |
8 |     let val = (s.func)();
  |               ^^^^^^^^ cannot move out of borrowed content

error: aborting due to 2 previous errors
Run Code Online (Sandbox Code Playgroud)

这是解决这个问题的唯一方法来将闭包存储在堆上(通过Box<FnOnce() -> u32>)吗?为什么调用一个闭包呢?据推测,它不会改变函数本身.

Fra*_*gné 5

封闭被移动,因为FnOnce::call_once需要self的值.该合同强制保证不会多次调用该函数.

如果你确实最多只调用一次闭包,而你想要使用FnOncetrait,那么你的struct需要获得该闭包的所有权(并且你需要在闭包类型上使你的struct通用).请注意,调用闭包会将闭包移出结构,从而使整个结构无效; 你可以解决这个问题,方法是在调用它之前将其封闭FnOnce在一个Option和之后.takeOption

如果你可能多次调用闭包,你不想取得闭包的所有权,或者你不想在闭包类型上使你的结构是通用的,那么你应该使用Fn或者FnMut代替.Fn::callself由参考和FnMut::call_mutself由可变引用.由于两者都接受引用,因此可以使用特征对象.