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

Struct 调用存储在Rust中的结构中的堆栈分配闭包,struct,rust,closures,move,Struct,Rust,Closures,Move,我将闭包存储在结构中,如下所示: #[derive(Clone)] struct S<'a> { func: &'a FnOnce() -> u32 } fn main() { let s = S { func: &|| 0 }; let val = (s.func)(); println!("{}", val); } trait Callable { fn call_once_safe(&mut self,

我将闭包存储在结构中,如下所示:

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

fn main() {
    let s = S { func: &|| 0 };
    let val = (s.func)();
    println!("{}", val);
}
trait Callable {
    fn call_once_safe(&mut self, default: u32) -> u32;
}

impl<F: FnOnce() -> u32> Callable for Option<F> {
    fn call_once_safe(&mut self, default: u32) -> u32 {
        if let Some(func) = self.take() {
            func()
        } else {
            default
        }
    }
}

struct S<'a> {
    func: &'a mut Callable
}

impl<'a> S<'a> {
    pub fn invoke(&mut self) -> u32 {
        self.func.call_once_safe(1)
    }
}

fn main() {
    let mut s = S { func: &mut Some(|| 0) };
    let val1 = s.invoke();
    let val2 = s.invoke();
    println!("{} {}", val1, val2);
}

这是将闭包存储在堆上的唯一方法(通过
Box u32>
)吗?为什么要求停业会影响它?大概调用它不会改变函数本身。

闭包正在移动,因为它按值获取
self
。本合同强制保证函数不会被多次调用

如果您最多只调用一次闭包,并且希望使用
FnOnce
特性,那么您的结构需要拥有该闭包的所有权(并且您需要使您的结构成为闭包类型的泛型结构)。请注意,调用闭包会将闭包移出结构,从而使整个结构无效;您可以通过将
FnOnce
包装在
选项中,并在调用它之前从
选项中取出闭包来解决这个问题


如果您可能不止一次调用闭包,您不想拥有闭包的所有权,或者您不想使结构成为闭包类型的泛型,那么您应该使用
Fn
FnMut
。通过引用获取
self
,并通过可变引用获取
self
。因为两者都接受引用,所以您可以使用trait对象。

正如Francis所解释的,声明闭包
FnOnce
告诉Rust您接受最广泛的闭包类,包括那些耗尽它们捕获的对象的闭包。编译器通过在调用时销毁闭包对象本身(将其移动到自己的
call
方法)来确保只调用此类闭包一次

可以在闭包上使用
FnOnce
并且仍然没有
S
generic,但是需要进行一些工作来设置,以便不能多次调用闭包:

  • 闭包必须存储在
    选项
    中,因此其内容可能会“被盗”,而
    选项
    将替换为
    (此部分确保闭包不会被调用两次)
  • 发明一种特性,它知道如何从选项中窃取闭包并调用它(或者如果闭包已经被窃取,则执行其他操作)
  • S
    中存储对trait对象的引用-这使得相同的
    S
    类型可以在不同的闭包上工作,而不会在闭包类型上是泛型的
结果如下所示:

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

fn main() {
    let s = S { func: &|| 0 };
    let val = (s.func)();
    println!("{}", val);
}
trait Callable {
    fn call_once_safe(&mut self, default: u32) -> u32;
}

impl<F: FnOnce() -> u32> Callable for Option<F> {
    fn call_once_safe(&mut self, default: u32) -> u32 {
        if let Some(func) = self.take() {
            func()
        } else {
            default
        }
    }
}

struct S<'a> {
    func: &'a mut Callable
}

impl<'a> S<'a> {
    pub fn invoke(&mut self) -> u32 {
        self.func.call_once_safe(1)
    }
}

fn main() {
    let mut s = S { func: &mut Some(|| 0) };
    let val1 = s.invoke();
    let val2 = s.invoke();
    println!("{} {}", val1, val2);
}
trait可调用{
fn调用一次\u safe(&mut self,默认值:u32)->u32;
}
impl u32>可为选项调用{
fn调用一次\u safe(&mut self,默认值:u32)->u32{
如果让一些(func)=self.take(){
func()
}否则{
违约
}
}
}
结构{
发布fn调用(&mut self)->u32{
自我功能安全呼叫一次(1)
}
}
fn main(){
设muts=s{func:&mutsome(| | 0)};
设val1=s.invoke();
设val2=s.invoke();
println!(“{}{}”,val1,val2);
}

唯一知道闭包细节的地方是为特定的
选项
实现
Callable
,为每个闭包生成,并由初始化
S
中的
func
时创建的
&mut Callable
胖指针的vtable引用。这有助于加载,谢谢。我最终使用了
Fn
而不是
FnOnce
,因为我想创建该结构的const实例,但整个响应有助于理解整个过程。