为什么不需要将此堆栈变量移到闭包中?

时间:2019-10-27 23:06:10

标签: curl rust

我想了解为什么下面的示例中的外部堆栈变量data不需要move关键字与闭包一起使用。

该示例取自Curl crate,而documentation声明了write_function的生存期取自transfer变量,因此,闭包可以访问堆栈变量data而无需移动它。

以下是该文档的引文:

  

请注意,此函数的生命周期是“静态的”,但这通常过于严格。要使用堆栈数据,请考虑先调用传输方法,然后使用write_function来配置可以引用堆栈本地数据的回调。

请参阅:https://docs.rs/curl/0.4.6/curl/easy/struct.Easy.html#method.write_function

use curl::easy::Easy;

let mut data = Vec::new();
let mut handle = Easy::new();
handle.url("https://www.rust-lang.org/").unwrap();
{
    let mut transfer = handle.transfer();
    transfer.write_function(|new_data| {
        data.extend_from_slice(new_data);
        Ok(new_data.len())
    }).unwrap();
    transfer.perform().unwrap();
}
println!("{:?}", data);

为什么这样做?

或者,如果我尝试直接使用write_function中的handle,则会出现data被移动的借用错误。

这是一个不起作用的示例,我理解为什么它不起作用。我很困惑为什么上面的方法代替了。

use curl::easy::Easy;

let mut data = Vec::new();
let mut handle = Easy::new();
handle.url("https://www.rust-lang.org/").unwrap();
handle.write_function(move |new_data| {
   data.extend_from_slice(new_data);
   Ok(new_data.len())
}).unwrap();
handle.perform().unwrap();
println!("{:?}", data);
//               ^^^^ error because it was moved

2 个答案:

答案 0 :(得分:2)

您引用的curl文档来自curl::easy::Easy::write_function方法。如前所述,此函数采用一个闭包,闭包的生存期为'static-换句话说,它必须在程序的整个过程中持续有效。

这意味着此类闭包借用的任何值都永远不会返回,因为闭包永远不会超出范围。

这与curl rust库包装curl C库这一事实有关,并且C库的工作原理是允许调用者为各种事件注册回调函数。在将闭包作为回调函数传递给C库之后,rust编译器无法跟踪其寿命,因此,唯一可以使用的安全寿命是'static

要解决此问题,curl模块提供了一个Transfer对象,您可以向其注册寿命少于'static的闭包。 Transfer对象负责在基础C库中注册和注销回调,并注意其生命周期。

了解到这一点后,请考虑您的示例代码:

{
    let mut transfer = handle.transfer();
    transfer.write_function(|new_data| {
        data.extend_from_slice(new_data);
        Ok(new_data.len())
    }).unwrap();
    transfer.perform().unwrap();
}

在这里,闭包可变地借入data。闭包将传递到transfer.write_function,这要求它要持续到传输对象本身为止。因此,将借用data直到声明了transfer的块的末尾。

答案 1 :(得分:1)

在您的第一个示例中,生锈使&mut data穿过堆栈,并继续让<main>拥有data

在第二个示例中,rust通过handle.write_function(move |new_data| {关键字赋予datamove的所有权,然后将内部内容写入data,并在作用域末尾丢弃}).unwrap();,然后在底部尝试通过data再次阅读println!,这要求rust检索已被丢弃的东西。