References to captured variables in an Actix server causes "argument requires that it must outlive 'static" - rust

In the below example, I have parameters verbose and data_source that are set by command line parameters. verbose is a boolean flag, but data_source is used to select a default from a set of available functions that can be used depending on the data source for the application.
Actix uses a closure to set up a server, so I need to get these parameters into the closure. I added move for the bool, but I'm having trouble passing in the function to be used for the index and am getting lifetime errors. I've tried boxing the function, but that doesn't seem to help.
If I'm understanding the error message correctly, it's actually the closure itself that is failing to outlive 'static.
What should I do to solve this issue?
extern crate actix;
extern crate actix_web;
extern crate env_logger;
use actix_web::http::Method;
use actix_web::{middleware, server, App, HttpRequest, HttpResponse};
enum DataSource {
Postgres,
HDF5,
}
fn index_postgres(req: &HttpRequest) -> HttpResponse {
HttpResponse::Ok().body("not implemented")
}
fn index_hdf5(req: &HttpRequest) -> HttpResponse {
HttpResponse::Ok().body("not implemented")
}
fn main() {
let mut verbose = false;
verbose = true;
let mut data_source = DataSource::Postgres;
data_source = DataSource::HDF5;
let index = match data_source {
DataSource::Postgres => index_postgres,
DataSource::HDF5 => index_hdf5,
};
::std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
let sys = actix::System::new("test");
server::new(move || {
if verbose {
App::new()
.middleware(middleware::Logger::default())
.resource("/", |r| r.method(Method::GET).f(index))
} else {
App::new().resource("/", |r| r.method(Method::GET).f(index))
}
})
.bind("127.0.0.1:8080")
.unwrap()
.start();
println!("Started http server: 127.0.0.1:8080");
let _ = sys.run();
}
error: unsatisfied lifetime constraints
--> src\main.rs:50:13
|
48 | server::new(move || {
| ------- lifetime `'1` represents this closure's body
49 | if verbose {
50 | / App::new()
51 | | .middleware(middleware::Logger::default())
52 | | .resource("/", |r| r.method(Method::GET).f(index))
| |__________________________________________________________________^ argument requires that `'1` must outlive `'static`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure

I don't understand what compiler is trying to say but it obvious that you need move in handler closures:
if verbose {
App::new()
.middleware(middleware::Logger::default())
.resource("/", move |r| r.method(Method::GET).f(index))
} else {
App::new().resource("/", move |r| r.method(Method::GET).f(index))
}
This is because you need to move index from the outer closure.

Related

Share state between actix-web server and async closure

I want to periodically fetch data (using asynchronous reqwest), which is then served at an http endpoint using actix-web as a server.
(I have a data source that has a fixed format, that I want to have read by a service that require a different format, so I need to transform the data.)
I've tried to combine actix concepts with the thread sharing state example from the Rust book, but I don't understand the error or how to solve it.
This is the code minified as much as I was able:
use actix_web::{get, http, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Mutex<AppState>>) -> impl Responder {
let state = data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.to_owned())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let status_string = get_state().await.unwrap();
let app_data = Arc::new(Mutex::new(web::Data::new(AppState {
status: status_string,
})));
let app_data1 = Arc::clone(&app_data);
actix_web::rt::spawn(async move {
loop {
println!("I get executed every 2-ish seconds!");
sleep(Duration::from_millis(2000)).await;
let res = get_state().await;
let mut app_data = app_data1.lock().unwrap();
// Edit 2: this line is not accepted by the compiler
// Edit 2: *app_data.status = res.unwrap();
// Edit 2: but this line is accepted
*app_data = web::Data::new(AppState { status: res });
}
});
let app_data2 = Arc::clone(&app_data);
// Edit 2: but I get an error here now
HttpServer::new(move || App::new().app_data(app_data2).service(index))
.bind(("127.0.0.1", 9090))?
.run()
.await
}
async fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new().get("http://ipecho.net/plain".to_string());
let status = client.send().await?.text().await?;
println!("got status: {status}");
Ok(status)
}
But I get the following error:
error[E0308]: mismatched types
--> src/main.rs:33:32
|
33 | *app_data.status = res.unwrap();
| ---------------- ^^^^^^^^^^^^ expected `str`, found struct `String`
| |
| expected due to the type of this binding
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> src/main.rs:33:13
|
33 | *app_data.status = res.unwrap();
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: the left-hand-side of an assignment must have a statically known size
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
Why do I suddenly get a str? Is there an easy fix or is my approach to solving this wrong?
Edit: Maybe removing the * is the right way to go, as Peter Hall suggests, but that gives me the following error instead:
error[E0594]: cannot assign to data in an `Arc`
--> src/main.rs:33:13
|
33 | app_data.status = res.unwrap();
| ^^^^^^^^^^^^^^^ cannot assign
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<AppState>`
error[E0507]: cannot move out of `app_data2`, a captured variable in an `Fn` closure
--> src/main.rs:38:49
|
37 | let app_data2 = Arc::clone(&app_data);
| --------- captured outer variable
38 | HttpServer::new(move || App::new().app_data(app_data2).service(index))
| ------- ^^^^^^^^^ move occurs because `app_data2` has type `Arc<std::sync::Mutex<Data<AppState>>>`, which does not implement the `Copy` trait
| |
| captured by this `Fn` closure
Some errors have detailed explanations: E0507, E0594.
For more information about an error, try `rustc --explain E0507`.
Edit 2: I now get the following error (code changes commented with 'Edit 2' above):
error[E0507]: cannot move out of `app_data2`, a captured variable in an `Fn` closure
--> src/main.rs:46:49
|
45 | let app_data2 = app_data.clone();
| --------- captured outer variable
46 | HttpServer::new(move || App::new().app_data(app_data2).service(index))
| ------- ^^^^^^^^^ move occurs because `app_data2` has type `Arc<Mutex<Data<AppState>>>`, which does not implement the `Copy` trait
| |
| captured by this `Fn` closure
For more information about this error, try `rustc --explain E0507`.
My Cargo.toml dependencies:
[dependencies]
actix-web = "4.2.1"
reqwest = "0.11.12"
tokio = "1.21.2"
async solution
I had my types mixed up a bit, having the app state as Arc<Mutex<T>> seemed to be the way to go, maybe it would be better with Arc<RwLock<T>>.
use actix_web::{get, http, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Arc<Mutex<AppState>>>) -> impl Responder {
let state = data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.to_owned())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let status_string = get_state().await.unwrap();
let app_data = Arc::new(Mutex::new(AppState {
status: status_string,
}));
let app_data1 = app_data.clone();
actix_web::rt::spawn(async move {
loop {
println!("I get executed every 2-ish seconds!");
sleep(Duration::from_millis(2000)).await;
let res = get_state().await.unwrap();
let mut app_data = app_data1.lock().unwrap();
*app_data = AppState { status: res };
}
});
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_data.clone()))
.service(index)
})
.bind(("127.0.0.1", 9090))?
.run()
.await
}
async fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new().get("http://ipecho.net/plain".to_string());
let status = client.send().await?.text().await?;
println!("got status: {status}");
Ok(status)
}
async/sync solution
Instead of doing the async get with reqwest I have a solution with the synchronous crate minreq (that I found after a lot of searching). I also chose to not use the #[actix_web::main] macro, and instead start the runtime explicitly at the end of my main function.
use actix_web::{get, http, rt, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Arc<Mutex<AppState>>>) -> impl Responder {
let state = &data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.clone())
}
fn main() -> std::io::Result<()> {
let status_string = get_state().unwrap();
let app_data = Arc::new(Mutex::new(AppState {
status: status_string,
}));
let app_data1 = Arc::clone(&app_data);
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(2000));
let res = get_state().unwrap();
let mut app_data = app_data1.lock().unwrap();
*app_data = AppState { status: res };
});
rt::System::new().block_on(
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_data.clone()))
.service(index)
})
.bind(("127.0.0.1", 9090))?
.run(),
)
}
fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let resp = minreq::get("http://ipecho.net/plain").send().unwrap();
let state = resp.as_str().unwrap();
Ok(state.to_string())
}

How can a reference escape a closure? `packet` escapes the closure body here

In the following example, I don t understand why it complains about packet escaping the closure. Yes, packet has a short lifetime, it lives only as long as on_packet function call lasts. However, when I call on_packet_render(&packet), I'm not moving packet, I'm just passing a reference, which will be used and the on_packet_render call will return while I'm inside on_packet, so I don't see why this error.
use std::sync::Arc;
pub type DecoderProvider = Arc<dyn Fn(&dyn Fn(&Option<&Arc<u8>>))>;
trait DecodedPacket<'a, T> {}
fn main() {
let mut on_packet_render = |packet: &Option<Box<dyn DecodedPacket<u8>>>| {
if let Some(packet) = packet {
}
};
let mut on_packet: Arc<
dyn for<'c, 'd> FnMut(
Option<Box<dyn DecodedPacket<'c, u8> + 'd>>,
)> = Arc::new(|packet| {
on_packet_render(&packet);
});
}
Playground
Error:
error[E0521]: borrowed data escapes outside of closure
--> src/main.rs:15:9
|
6 | let mut on_packet_render = |packet: &Option<Box<dyn DecodedPacket<u8>>>| {
| -------------------- `on_packet_render` declared here, outside of the closure body
...
14 | )> = Arc::new(|packet| {
| ------ `packet` is a reference that is only valid in the closure body
15 | on_packet_render(&packet);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `packet` escapes the closure body here

Unable to specify lifetime parameter to solve compilation error

I'm learning Rust by doing small stuffs. I'm currently writing this app so, its first step is to read a config.json file, but I'm having this compilation error that I'm unable to resolve.
Here's my Cargo.toml dependencies
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
regex = "1.4.3"
Here's the code
use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use regex::Regex;
use serde_json::Value;
fn _get_config() -> Box<dyn FnMut() -> &Value> {
let mut config = Box::new(Value::Null);
let a = || {
if *config == Value::Null {
match File::open("config.json").and_then(|file| -> Result<Value, std::io::Error> {
serde_json::from_reader(BufReader::new(file)).map_err(|e| e.into())
}) {
Ok(v) => *config = v,
Err(_) => {
*config = serde_json::from_str(
r#"
{
"DOMAIN_AS_ROOT_FOLDER": false,
"secret": "abcxyz"
}
"#,
)
.expect("Cannot initialize config, abort !");
}
}
}
config.as_ref()
};
Box::new(a)
}
fn main() {
let get_config = _get_config();
get_config();
}
And here's the compilation error
❯ cargo run
error[E0106]: missing lifetime specifier
--> src/main.rs:9:40
|
9 | fn _get_config() -> Box<dyn FnMut() -> &Value> {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
|
9 | fn _get_config() -> Box<dyn for<'a> FnMut() -> &'a Value> {
| ^^^^^^^ ^^^
help: consider using the `'static` lifetime
|
9 | fn _get_config() -> Box<dyn FnMut() -> &'static Value> {
| ^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0106`.
error: could not compile `sieve_generator`
To learn more, run the command again with --verbose.
Basically the _get_config() returns a closure that allows me to get the config object everytime I call it. I don't see why there is such error since the variable config is supposed to live as long as my closure, there's nothing else as parameters here, why does it requires a lifetime here ? And how do I fix it ?
Thank you all very much for your time. I appreciate it a lot.
You'll never be able to do what you want as you can't guarantee to the compiler that the closure will NEVER go out of scope for the applications life time (which you'd have to do in this case because the compiler has no idea how long you'll hold on to the &Value reference).
Instead, since it looks like you're reading the configuration from disk only once why not store it in a static variable with the help of the lazy_static crate.
use std::fs::File;
use std::io::BufReader;
use serde_json::Value;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
lazy_static! {
static ref CONFIG: Arc<Mutex<Value>> = {
let file: std::io::Result<Value> = File::open( "config.json" ).and_then( | f | {
serde_json::from_reader( BufReader::new( f ) )
.map_err( | e | e.into( ) )
} );
Arc::new( Mutex::new( match file {
Ok( v ) => v,
_ => {
serde_json::from_str(
r#"
{
"DOMAIN_AS_ROOT_FOLDER": false,
"secret": "abcxyz"
}
"#
)
.expect( "Cannot initialize config, abort !" )
}
} ) )
};
}
fn main( ) {
let config = CONFIG.lock( ).unwrap( );
// Use your config here.
}
EDIT:
The Arc and Mutex are only necessary if you plan on mutating the Value. If you are not going to mutate the configuration at some point then you can ditch the use the of Arc and Mutex.
In both examples I removed the Option type as it wasn't needed.
lazy_static! {
static ref CONFIG: Value = {
let file: std::io::Result<Value> = File::open( "config.json" ).and_then( | f | {
serde_json::from_reader( BufReader::new( f ) )
.map_err( | e | e.into( ) )
} );
match file {
Ok( v ) => v,
_ => {
serde_json::from_str(
r#"
{
"DOMAIN_AS_ROOT_FOLDER": false,
"secret": "abcxyz"
}
"#
)
.expect( "Cannot initialize config, abort !" )
}
}
};
}

Error on Future generator closure: Captured variable cannot escape `FnMut` closure body

I want to create a simple websocket server. I want to process the incoming messages and send a response, but I get an error:
error: captured variable cannot escape `FnMut` closure body
--> src\main.rs:32:27
|
32 | incoming.for_each(|m| async {
| _________________________-_^
| | |
| | inferred to be a `FnMut` closure
33 | | match m {
34 | | // Error here...
35 | | Ok(message) => do_something(message, db, &mut outgoing).await,
36 | | Err(e) => panic!(e)
37 | | }
38 | | }).await;
| |_____^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
This gives a few hits on Stack Overflow but I don't see anywhere in my code where a variable is escaping. The async block won't run concurrently, so I don't see any problem. Furthermore, I feel like I am doing something very simple: I get a type which allows me to send data back to the client, but when using a reference to it in the async block, it gives a compile error. The error only occurs when I use the outgoing or db variable in the async code.
This is my code (error is in the handle_connection function):
main.rs
use tokio::net::{TcpListener, TcpStream};
use std::net::SocketAddr;
use std::sync::Arc;
use futures::{StreamExt, SinkExt};
use tungstenite::Message;
use tokio_tungstenite::WebSocketStream;
struct DatabaseConnection;
#[tokio::main]
async fn main() -> Result<(), ()> {
listen("127.0.0.1:3012", Arc::new(DatabaseConnection)).await
}
async fn listen(address: &str, db: Arc<DatabaseConnection>) -> Result<(), ()> {
let try_socket = TcpListener::bind(address).await;
let mut listener = try_socket.expect("Failed to bind on address");
while let Ok((stream, addr)) = listener.accept().await {
tokio::spawn(handle_connection(stream, addr, db.clone()));
}
Ok(())
}
async fn handle_connection(raw_stream: TcpStream, addr: SocketAddr, db: Arc<DatabaseConnection>) {
let db = &*db;
let ws_stream = tokio_tungstenite::accept_async(raw_stream).await.unwrap();
let (mut outgoing, incoming) = ws_stream.split();
// Adding 'move' does also not work
incoming.for_each(|m| async {
match m {
// Error here...
Ok(message) => do_something(message, db, &mut outgoing).await,
Err(e) => panic!(e)
}
}).await;
}
async fn do_something(message: Message, db: &DatabaseConnection, outgoing: &mut futures_util::stream::SplitSink<WebSocketStream<TcpStream>, Message>) {
// Do something...
// Send some message
let _ = outgoing.send(Message::Text("yay".to_string())).await;
}
Cargo.toml
[dependencies]
futures = "0.3.*"
futures-channel = "0.3.*"
futures-util = "0.3.*"
tokio = { version = "0.2.*", features = [ "full" ] }
tokio-tungstenite = "0.10.*"
tungstenite = "0.10.*"
When using async move, I get the following error:
code
incoming.for_each(|m| async move {
let x = &mut outgoing;
let b = db;
}).await;
error
error[E0507]: cannot move out of `outgoing`, a captured variable in an `FnMut` closure
--> src\main.rs:33:38
|
31 | let (mut outgoing, incoming) = ws_stream.split();
| ------------ captured outer variable
32 |
33 | incoming.for_each(|m| async move {
| ______________________________________^
34 | | let x = &mut outgoing;
| | --------
| | |
| | move occurs because `outgoing` has type `futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio::net::tcp::stream::TcpStream>, tungstenite::protocol::message::Message>`, which does not implement the `Copy` trait
| | move occurs due to use in generator
35 | | let b = db;
36 | | }).await;
| |_____^ move out of `outgoing` occurs here
FnMut is an anonymous struct, since FnMutcaptured the &mut outgoing, it becomes a field inside of this anonymous struct and this field will be used on each call of FnMut , it can be called multiple times. If you lose it somehow (by returning or moving into another scope etc...) your program will not able to use that field for further calls, due to safety Rust Compiler doesn't let you do this(for your both case).
In your case instead of capturing the &mut outgoing we can use it as argument for each call, with this we'll keep the ownership of outgoing. You can do this by using fold from futures-rs:
incoming
.fold(outgoing, |mut outgoing, m| async move {
match m {
// Error here...
Ok(message) => do_something(message, db, &mut outgoing).await,
Err(e) => panic!(e),
}
outgoing
})
.await;
This may seem a bit tricky but it does the job, we are using constant accumulator(outgoing) which will be used as an argument for our FnMut.
Playground (Thanks #Solomon Ucko for creating reproducible example)
See also :
How to return the captured variable from `FnMut` closure, which is a captor at the same time
How can I move a captured variable into a closure within a closure?

Two closures that use references to parent scope

Im trying to create a struct that stores 2 closures, each with read references to an parent scope variable where they were created.
After I have closed over the references in the parent scope, how do I make sure they live for the same amount of time as the closures?
For example:
struct S {
x: bool,
}
type Closure = Box<dyn Fn()>;
struct Env {
fn_a: Closure,
fn_b: Closure,
}
fn get_env() -> Env {
let s = S { x: true };
let fn_a = || {
&s.x;
};
let fn_b = || {
&s.x;
};
Env {
fn_a: Box::new(fn_a),
fn_b: Box::new(fn_b),
}
}
fn main() {
let env = get_env();
}
playground
Results in:
error[E0597]: `s` does not live long enough
--> src/main.rs:16:10
|
15 | let fn_a = || {
| -- value captured here
16 | &s.x;
| ^ borrowed value does not live long enough
...
23 | fn_a: Box::new(fn_a),
| -------------- cast requires that `s` is borrowed for `'static`
...
26 | }
| - `s` dropped here while still borrowed```
It depends on your scenario. How long would the returned closures live?
If it lives as long as the Env value, what about moving s into the Env value and accessing s from the Env value?
If you don't know (or don't want to spend the trouble to figure out) the lifetime for the closures, a convenient (although slightly less efficient) way is to use Rc, then move a clone of the Rc from the results:
Playground
use std::rc::Rc;
pub struct S {
x: bool,
}
pub type Closure = Box<dyn Fn()>;
#[allow(unused)]
pub struct Env {
fn_a: Closure,
fn_b: Closure,
}
fn get_env() -> Env {
let s = Rc::new(S { x: true });
let cloned = Rc::clone(&s);
let fn_a = move || {
s.x;
};
let fn_b = move || {
cloned.x;
};
Env {
fn_a: Box::new(fn_a),
fn_b: Box::new(fn_b),
}
}
fn main() {
let _env = get_env();
}

Resources