3

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
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
fragsalat
  • 500
  • 4
  • 14
  • 1
    It looks like your question might be answered by the answers of [Is there another option to share an Arc in multiple closures besides cloning it before each closure?](https://stackoverflow.com/q/31360003/155423) and [Is there any way to return a reference to a variable created in a function?](https://stackoverflow.com/q/32682876/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Feb 11 '20 at 21:40
  • [The duplicate applied to your code](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f6864a1914a5c705429c32de925a0697) – Shepmaster Feb 11 '20 at 21:43
  • The first one helped and seem to apply to my issue. Thanks for helping :) Can be marked as duplicate then – fragsalat Feb 11 '20 at 22:42

0 Answers0