Search code examples
rusthyper

Borrowed value does not live enough?


This is the continuation of `RefCell<std::string::String>` cannot be shared between threads safely?, made a new Q for better presentation.

I made a minimal main with a Mutex, but now test_for_closure does not live long enough and is dropped here while still borrowed. What a ride! :)

Rust playground link: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=7bf56545350624e75aafa10524ea59ff

use std::{convert::Infallible, net::SocketAddr};
use std::sync::{Arc, Mutex};

use hyper::{Body, Request, Response, Server, Version};
use hyper::{Method, StatusCode};
use hyper::service::{make_service_fn, service_fn};
// use tokio::sync::Mutex;

#[tokio::main]
use std::{convert::Infallible, net::SocketAddr};
use std::sync::{Arc, Mutex};

use hyper::{Body, Request, Response, Server, Version};
use hyper::service::{make_service_fn, service_fn};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut test: Arc<Mutex<String>> = Arc::new(Mutex::from("Foo".to_string()));
    let mut test_for_closure = Arc::clone(&test);

    let addr = SocketAddr::from(([127, 0, 0, 1], 4321));

    let make_svc = make_service_fn(|_conn| async {
        Ok::<_, Infallible>(service_fn(|req: Request<Body>| async move {
            if req.version() == Version::HTTP_11 {
                let foo = test_for_closure.lock().unwrap();
                Ok(Response::new(Body::from(foo.as_str())))
            } else {
                Err("not HTTP/1.1, abort connection")
            }
        }))
    });

    let server = Server::bind(&addr).serve(make_svc);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }

    Ok(())
}

enter image description here

How can this be addressed, considering that we will need to be able to modify the Arc's value eventually in main but still have hyper send back the "current" value on an HTTP response?


Solution

  • There are two issues, first of all, Body only implements From<&'static str> but the given &str is bound to the lifetime on the MutexGuard, therefore the Body::from call fails with a lifetime error. You can work around this via foo.clone().

    The second issue is about the multiple nested scopes which would require additional clone()s on the Arc<Mutex<String>> and move on the service_fn closure. The following compiles:

    use std::sync::{Arc, Mutex};
    use std::{convert::Infallible, net::SocketAddr};
    
    use hyper::service::{make_service_fn, service_fn};
    use hyper::{Body, Request, Response, Server, Version};
    
    #[tokio::main]
    async fn main() -> Result<(), Box<dyn std::error::Error>> {
        let test: Arc<Mutex<String>> = Arc::new(Mutex::from("Foo".to_string()));
    
        let addr = SocketAddr::from(([127, 0, 0, 1], 4321));
    
        let make_svc = make_service_fn(|_conn| {
            let test_for_closure = test.clone();
            let svc_fn = service_fn(move |req: Request<Body>| {
                let test_for_closure = test_for_closure.clone();
                async move {
                    if req.version() == Version::HTTP_11 {
                        let foo = test_for_closure.lock().unwrap();
                        Ok(Response::new(Body::from(foo.clone())))
                    } else {
                        Err("not HTTP/1.1, abort connection")
                    }
                }
            });
            async move { Ok::<_, Infallible>(svc_fn) }
        });
    
        let server = Server::bind(&addr).serve(make_svc);
    
        // change the value at will:
        let mut guard = test.lock().unwrap();
        *guard = "Bar".into();
        drop(guard);
    
        if let Err(e) = server.await {
            eprintln!("server error: {}", e);
        }
    
        Ok(())
    }
    

    Playground