在闭包中调用堆函数参数



我正在使用 rust 0.8。

为什么我可以这样做:

fn add(num: ~int) -> ~fn(int) -> int { |x|
    *num + x
}

但不是这个:

fn outer(num: ~int) -> ~fn(int) -> int { |x|
    *inner(num) + x
}
fn inner(num: ~int) -> ~int {
    num
}

第二个失败,并显示"错误:无法移出堆闭包中捕获的外部变量"。 是什么让调用函数变得特别?

是否担心内部函数可能会对静态分析无法捕获的盒装函数执行一些脏操作?

问题是可以调用闭包两次。在第一次运行时,捕获的变量num被移动到inner,即移出闭包的环境。然后,在第二次调用时,num所在的位置现在无效(因为它已被移出),这会破坏内存安全。

更详细地说,可以将闭包视为(大约)

struct Closure { // stores all the captured variables
    num: ~int
}
impl Closure {
    fn call(&self, x: int) -> int { 
        // valid:
        *self.num + x
        // invalid:
        // *inner(self.num) + x
    }
}

希望这能更清楚地说明:在无效的调用中,人们试图将self.num从借用的指针后面移出到inner调用中(之后它与num字段完全断开连接)。如果这是可能的,那么self将处于无效状态,因为,例如,self.num 上的析构函数可能已被调用以释放内存(违反内存安全)。


一种解决方法是"once函数",它被实现但隐藏在编译器标志后面,因为它们可能会被删除,以支持(最基本的)将上面的call类型调整为fn call(self, x: int),也就是说,调用闭包移动self这意味着您可以移出环境(因为call随后拥有self及其字段),也意味着该函数是静态保证只调用一次*。

*如果关闭的环境不移动所有权,例如,如果它被struct Env { x: int },则不完全正确。

最新更新