rust - 如何将生命周期设置为闭包中捕获的值?

标签 rust closures lifetime borrowing

我写了我认为很简单的代码:

#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;
extern crate statsd;

use rocket::{Data, Request};
use rocket::fairing::AdHoc;
use statsd::Client;

#[get("/")]
fn index() -> &'static str {
    "Hello, World"
}

fn main() {
    let mut client = Client::new("127.0.0.1:9125", "miniserver-rs").unwrap();

    rocket::ignite()
        .attach(AdHoc::on_request(|request, data|{
            client.incr("http.requests");
            println!("Request URI: {}", request.uri());
        }))
       .mount("/", routes![index])
       .launch();
   client.incr("server.bootstrap");
}

我尝试在每个请求上发送一些指标,但我收到以下编译器错误:

   Compiling miniserver-rs v0.1.0 (main.rs)
error[E0373]: closure may outlive the current function, but it borrows `client`, which is owned by the current function
  --> src\main.rs:19:33
   |
19 |       .attach(AdHoc::on_request(|request, _data|{
   |                                 ^^^^^^^^^^^^^^^^ may outlive borrowed value `client`
20 |           client.incr("http.requests");
   |           ------ `client` is borrowed here help: to force the closure to take ownership of `client` (and any other referenced variables), use the `move` keyword
   |
19 |       .attach(AdHoc::on_request(move |request, _data|{
   |                                 ^^^^^^^^^^^^^^^^^^^^^

error[E0387]: cannot borrow data mutably in a captured outer variable in an `Fn` closure
  --> src\main.rs:20:11
   |
20 |           client.incr("http.requests");
   |           ^^^^^^
   |
help: consider changing this closure to take self by mutable reference
  --> src\main.rs:19:33
   |
19 |         .attach(AdHoc::on_request(|request, _data|{
   |  _________________________________^
20 | |           client.incr("http.requests");
21 | |           println!("Request URI: {}", request.uri());
22 | |       }))
   | |_______^

我知道 client 被捕获在一个闭包中并由另一个函数 (main) 拥有,该函数的生命周期可能比闭包短。我无法移动它,因为Client 没有实现Copy,因此以后无法使用该引用。

我也明白我不能在闭包中借用可变数据(Client 是可变的)。经过大量搜索,我可以得出结论,我需要结合使用 Arc/RcMutex/RwLock/RefCell,但在继续之前,我想确定它是必需的。

最佳答案

让我们看看要求。你想调用 statsd::Client::incr(&mut client, metric) 从闭包内部,所以你需要可变访问 client .这是您用 || 关闭的变量.

现在 AdHoc::on_request<F>(f: F) 需要 F: Fn(...) + Send + Sync + 'static . Fn意味着您只能通过 &self 对您的捕获进行不可变访问. 'static bound 意味着捕获本身不能是引用,因此它需要 move || .最后Sync意味着你不能使用 CellRefCell&self.client 获取可变引用,因为 Rocket 将在线程之间共享它。

正如您所怀疑的,通过 Send + Sync 共享可变访问的规范解决方案值是使用 Arc<Mutex<_>> .这样也解决了“搬家失联”的问题。您的代码如下所示(未经测试):

fn main() {
    let client = Arc::new(Mutex::new(
        Client::new("127.0.0.1:9125", "miniserver-rs").unwrap()));

    // shallow-clone the Arc to move it into closure
    let rocket_client = client.clone();
    rocket::ignite()
        .attach(AdHoc::on_request(move |request, data|{
            rocket_client.lock()
                .unwrap()
                .incr("http.requests");

            println!("Request URI: {}", request.uri());
        }))
       .mount("/", routes![index])
       .launch();

   client.lock()
       .unwrap()
       .incr("server.bootstrap");
}

关于rust - 如何将生命周期设置为闭包中捕获的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49797411/

相关文章:

rust/柴油 : How to query and insert into postgres tables which have uuid

for-loop - 如何在 vector 的元素上运行 for 循环并更改 for 循环内部和 for 循环外部的向量?

javascript - 在 jQuery 中获取变量

c++ - 临时绑定(bind)到聚合初始化结构成员的生命周期

rust - 为什么 Rust 中的泛型[有时]不需要生命周期说明符?

rust - 为什么不能在同一结构中存储值和对该值的引用?

rust - 如何防止 TcpStream 阻塞读取?

rust - 如何将盒装切片 (`Box<[T]>` ) 传递给 C 函数?

groovy - Groovy 闭包中的 If 语句

ios - 持有对使用闭包的本地对象的强引用 [Swift]