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(())
}
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?
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(())
}