【问题标题】:Why doesn't this stack variable need to be moved into the closure?为什么这个堆栈变量不需要移动到闭包中?
【发布时间】:2019-10-27 23:06:10
【问题描述】:

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

示例取自Curl cratedocumentation 声明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);

为什么会这样?

或者,如果我尝试直接使用handle 中的write_function,则会收到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

【问题讨论】:

标签: curl rust


【解决方案1】:

您引用的 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 的块结束。

【讨论】:

    【解决方案2】:

    在您的第一个示例中,rust 通过堆栈获取 &mut data,并继续让 <main> 拥有 data

    在您的第二个示例中,rust 通过move 关键字为handle.write_function(move |new_data| { 提供data 的所有权,然后内部写入data,并在范围}).unwrap(); 的末尾下降,然后在底部您尝试通过 println! 再次读取 data,这要求 rust 检索您已经要求删除的内容。

    【讨论】:

      猜你喜欢
      • 2021-07-28
      • 2021-12-30
      • 2018-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-04
      • 1970-01-01
      • 2014-10-28
      相关资源
      最近更新 更多