我想理解为什么下面的示例中的外部堆栈变量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
在您的第一个示例中,锈使&mut data
穿过堆栈,并继续让<main>
拥有data
。
[在您的第二个示例中,锈通过handle.write_function(move |new_data| {
关键字赋予data
所有权move
,然后内部将其写入data
,并放在范围}).unwrap();
的末尾,然后在底部试图通过data
再次读取println!
,它要求rust检索您已经要求将其删除的内容。
您引用的curl文档来自curl::easy::Easy::write_function
方法。如前所述,此函数采用一个闭包,闭包的生存期为'static
-换句话说,它必须在程序的整个过程中持续有效。
这意味着此类闭包借用的任何值都永远不会返回,因为闭包永远不会超出范围。
这与curl rust库包装了curl C库这一事实有关,并且C库的工作原理是允许调用者为各种事件注册回调函数。在将闭包作为回调函数传递给C库之后,rust编译器无法跟踪其有效期,因此,唯一可使用的安全有效期为'static
。
[要解决此问题,curl
模块提供了一个Transfer
对象,您可以向其注册寿命少于Transfer
的闭包。 'static
对象负责在基础C库中注册和注销回调,同时注意其生命周期。
了解了这一点,请考虑您的示例代码:
Transfer
此处,闭包可变地借入{
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
的块的末尾。