I have a global state which can be used down the code. For my hyper request handling, I generate a local state to group the data for easier access. I decided against a global static variable to use Arc<Mutex<_>>
for thread-safe access. Now I have issues with passing the Arc
down. I created example code which fails the same way. How do I fix and understand this?
use hyper::{
service::{make_service_fn, service_fn},
Body, Response, Server,
}; // 0.13.2
use std::{
process::exit,
sync::{Arc, Mutex},
};
struct GlobalState {
id: u64,
name: String,
}
impl GlobalState {
pub fn unregister(&self) {
println!("Unregister {}", self.id);
}
}
struct LocalState {
global_state: Arc<Mutex<GlobalState>>,
}
impl LocalState {
pub fn new(global_state: Arc<Mutex<GlobalState>>) -> Self {
LocalState { global_state }
}
pub fn name(&self) -> &str {
self.global_state.lock().unwrap().name.as_str()
}
}
async fn handle_request(local_state: LocalState) -> Result<Response<Body>, String> {
println!("Name {}", &local_state.name());
Ok(Response::new(Body::from("test".to_string())))
}
async fn start(port: u16, global_state: Arc<Mutex<GlobalState>>) -> Result<(), String> {
let service = make_service_fn(move |_| async {
Ok::<_, String>(service_fn(move |request| async {
let local_state = LocalState::new(global_state);
handle_request(local_state).await
}))
});
let addr = format!("0.0.0.0:{}", port).parse().unwrap();
let server = Server::bind(&addr).serve(service);
println!("Listening on {}", addr);
server.await.map_err(|error| format!("Error: {}", error))
}
#[tokio::main]
async fn main() {
let global_state = Arc::new(Mutex::new(GlobalState {
id: 1,
name: "name".to_string(),
}));
let gs_clone1 = global_state.clone();
let gs_clone2 = global_state.clone();
let server_handle = tokio::spawn(async {
if let Err(error) = start(8088, gs_clone1).await {
println!("Server failed: {}", error);
}
});
let abort_handle = tokio::spawn(async move {
if let Err(error) = tokio::signal::ctrl_c().await {
println!("Signal handling failed: {}", error);
}
println!("User terminated the process");
// Remove download server from list
gs_clone2.lock().unwrap().unregister();
exit(1);
});
tokio::try_join!(server_handle, abort_handle).unwrap();
}
It complains about a move because the closure gets called for every request and therefore it can not be moved multiple times. When I try to pass it as a reference, the borrow checker complains that .serve(service)
returns the 'static
lifetime.
warning: unused variable: `request`
--> src/main.rs:44:42
|
44 | Ok::<_, String>(service_fn(move |request| async {
| ^^^^^^^ help: consider prefixing with an underscore: `_request`
|
= note: `#[warn(unused_variables)]` on by default
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:32:9
|
32 | self.global_state.lock().unwrap().name.as_str()
| ---------------------------------^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| temporary value created here
error[E0507]: cannot move out of `global_state`, a captured variable in an `FnMut` closure
--> src/main.rs:44:57
|
42 | async fn start(port: u16, global_state: Arc<Mutex<GlobalState>>) -> Result<(), String> {
| ------------ captured outer variable
43 | let service = make_service_fn(move |_| async {
44 | Ok::<_, String>(service_fn(move |request| async {
| _________________________________________________________^
45 | | let local_state = LocalState::new(global_state);
| | ------------
| | |
| | move occurs because `global_state` has type `std::sync::Arc<std::sync::Mutex<GlobalState>>`, which does not implement the `Copy` trait
| | move occurs due to use in generator
46 | |
47 | | handle_request(local_state).await
48 | | }))
| |_________^ move out of `global_state` occurs here
error[E0507]: cannot move out of `global_state`, a captured variable in an `FnMut` closure
--> src/main.rs:43:50
|
42 | async fn start(port: u16, global_state: Arc<Mutex<GlobalState>>) -> Result<(), String> {
| ------------ captured outer variable
43 | let service = make_service_fn(move |_| async {
| __________________________________________________^
44 | | Ok::<_, String>(service_fn(move |request| async {
45 | | let local_state = LocalState::new(global_state);
| | ------------
| | |
| | move occurs because `global_state` has type `std::sync::Arc<std::sync::Mutex<GlobalState>>`, which does not implement the `Copy` trait
| | move occurs due to use in generator
46 | |
47 | | handle_request(local_state).await
48 | | }))
49 | | });
| |_____^ move out of `global_state` occurs here