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

时间:2018-04-12 13:07:38

标签: rust closures lifetime borrowing

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

WITH sample (customer_id, created_at) AS (
    VALUES
        (1000, '2017-12-29 20:48:54+00'::TIMESTAMP),
        (1000, '2017-12-30 12:48:56+00'),
        (1000, '2017-12-30 12:49:26+00'),
        (1002, '2017-12-30 12:52:36+00'),
        (1001, '2017-12-30 12:54:15+00'),
        (1002, '2017-12-30 13:54:15+00'),
        (1001, '2017-12-30 13:56:58+00'),
        (1000, '2018-01-02 13:01:13+00'),
        (1001, '2018-01-02 20:29:19+00'),
        (1002, '2018-01-02 20:29:31+00'),
        (1000, '2018-01-03 20:30:28+00'),
        (1001, '2018-01-03 20:38:40+00')    
)
SELECT 
    customer_id, 
    COUNT(DISTINCT created_at::DATE) 
FROM 
    sample 
GROUP BY 
    customer_id

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

#![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因为move没有实现Client,所以之后无法使用该引用。

我也明白我不能在闭包中借用可变数据(Copy是可变的)。经过大量搜索后,我可以得出结论,我需要将Client / ArcRc / Mutex / RwLock结合使用,但在进一步研究之前,我想确定它是必需的。

1 个答案:

答案 0 :(得分:3)

让我们来看看要求。您想从闭包内部调用statsd::Client::incr(&mut client, metric),因此您需要对client进行可变访问。这是您使用||关闭的变量。

现在AdHoc::on_request<F>(f: F)需要F: Fn(...) + Send + Sync + 'staticFn表示您只能通过&self获得对您的捕获的不可变访问权限。 'static绑定意味着捕获本身不能作为引用,因此它需要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");
}