我写了我认为很简单的代码:
#![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
/Rc
和 Mutex
/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
意味着你不能使用 Cell
或 RefCell
从 &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/