Unable to specify lifetime parameter to solve compilation error - rust

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 !" )
}
}
};
}

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

Rust "use of moved value" error with map_err and '?' operator but not with match return

I am trying to use the '?' operator and map_err to help flatten code that has a lot of simple matches that return on errors. The following is a code snippet to reproduce the compiler error I have been getting. I was wondering if anyone knows why compiler_error results in a 'use of moved value' error when no_compiler_error do not. They seem to have similar control flow and the function in map_err and after_test will never both be called, but there is clearly something I am missing.
fn main() {
no_compiler_error_1();
no_compiler_error_2();
compiler_error();
}
fn no_compiler_error_1() -> Result<(), String> {
let outer = String::from("Outer Error");
match test() {
Ok(()) => (),
Err(inner) => return Err(error_test(inner, outer))
};
after_test(outer);
Ok(())
}
fn no_compiler_error_2() -> Result<(), String> {
let outer = String::from("Outer Error");
test()?;
after_test(outer);
Ok(())
}
fn compiler_error() -> Result<(), String> {
let outer = String::from("Outer Error");
test().map_err(|inner| error_test(inner, outer))?;
after_test(outer);
Ok(())
}
fn test() -> Result<(), String> {
Err(String::from("Inner Error"))
}
fn error_test(inner: String, outer: String) -> String {
format!("{}:{}", inner, outer)
}
fn after_test(outer: String) {
format!("{} did not happen", outer);
}
error[E0382]: use of moved value: `outer`
--> src\main.rs:27:16
|
25 | let outer = String::from("Outer Error");
| ----- move occurs because `outer` has type `String`, which does not implement the `Copy` trait
26 | test().map_err(|inner| error_test(inner, outer))?;
| ------- ----- variable moved due to use in closure
| |
| value moved into closure here
27 | after_test(outer);
| ^^^^^ value used here after move
For more information about this error, try `rustc --explain E0382`.

Clashing types, crossterm::Result and core::Result error[E0107]:

I know the issue is that I have two Result types from different libraries but can't find how to fix it.
[dependencies]
crossterm = "0.23"
time = "0.3.9"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["blocking", "json"] }
use time::Instant;
use std::collections::HashMap;
use crossterm::{
event::{self, Event, KeyCode, KeyEvent},
Result,
};
pub fn read_char() -> Result<char> {
loop {
if let Event::Key(KeyEvent {
code: KeyCode::Char(c),
..
}) = event::read()?
{
return Ok(c);
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let instant = Instant::now();
let response = reqwest::blocking::get("https://httpbin.org/ip")?
.json::<HashMap<String, String>>()?;
let duration = instant.elapsed();
println!("ns = {:?}, response: {:#?}, ", duration.whole_nanoseconds(), response);
// Any key to continue
println!("Press any key to continue:");
println!("{:?}", read_char());
Ok(())
}
Gives the error:
error[E0107]: this type alias takes 1 generic argument but 2 generic arguments were supplied
--> src\main.rs:20:14
|
20 | fn main() -> Result<(), Box<dyn std::error::Error>> {
| ^^^^^^ -------------------------- help: remove this generic argument
| |
| expected 1 generic argument
How do I fix this? I have searched but am likely looking for incorrect terms e.g. namespace alias and core::Result error[E0107] is not really helping.
I have tried this without success:
fn main() -> core::Result<(), Box<dyn std::error::Error>> {
You have crossterm ::Result in scope, so you would have to disambiguate the result you want to return, otherwise it just thinks you want to return the crossterm type:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
...
Ok(())
}

How to use wirefilter over an infinite stream of data

I am writing a program to use wirefilter in order to filter data from an infinite stream.
But it seems that I cannot use a compiled ast in a loop because of lifetimes and when I try to compile, this is the output:
error: borrowed data cannot be stored outside of its closure
--> src/main.rs:34:33
|
31 | let filter = ast.compile();
| ------ ...so that variable is valid at time of its declaration
32 |
33 | for my_struct in data.filter(|my_struct| {
| ----------- borrowed data cannot outlive this closure
34 | let execution_context = my_struct.execution_context();
| ^^^^^^^^^ ----------------- cannot infer an appropriate lifetime...
| |
| cannot be stored outside of its closure
error: aborting due to previous error
error: Could not compile `wirefilter_playground`.
To learn more, run the command again with --verbose.
main.rs
use wirefilter::{ExecutionContext, Scheme};
lazy_static::lazy_static! {
static ref SCHEME: Scheme = Scheme! {
port: Int
};
}
#[derive(Debug)]
struct MyStruct {
port: i32,
}
impl MyStruct {
fn scheme() -> &'static Scheme {
&SCHEME
}
fn execution_context(&self) -> ExecutionContext {
let mut ctx = ExecutionContext::new(Self::scheme());
ctx.set_field_value("port", self.port).unwrap();
ctx
}
}
fn main() -> Result<(), failure::Error> {
let data = expensive_data_iterator();
let scheme = MyStruct::scheme();
let ast = scheme.parse("port in {2 5}")?;
let filter = ast.compile();
for my_struct in data.filter(|my_struct| {
let execution_context = my_struct.execution_context();
filter.execute(&execution_context).unwrap()
}).take(10) {
println!("{:?}", my_struct);
}
Ok(())
}
fn expensive_data_iterator() -> impl Iterator<Item=MyStruct> {
(0..).map(|port| MyStruct { port })
}
Cargo.toml
[package]
name = "wirefilter_playground"
version = "0.1.0"
edition = "2018"
[dependencies]
wirefilter-engine = "0.6.1"
failure = "0.1.5"
lazy_static = "1.3.0"
is it possible to make it work? I would like to yield only the filtered data for the final user otherwise the amount of data would be huge in memory.
Thank you in advance!
It looks like the problem is with the lifetime elision in return structs. In particular this code:
fn execution_context(&self) -> ExecutionContext {
//...
}
is equivalent to this one:
fn execution_context<'s>(&'s self) -> ExecutionContext<'s> {
//...
}
Which becomes obvious once you realize that ExecutionContext has an associated lifetime.
The lifetime of ExecutionContext does not have to match that of the MyStruct so you probably want to write:
fn execution_context<'e>(&self) -> ExecutionContext<'e> {
//...
}
or maybe:
fn execution_context<'s, 'e>(&'s self) -> ExecutionContext<'e>
where 'e: 's {
//...
}
depending on whether your context will eventually refer to any content of MyStruct.

Fighting with the Rust borrow checker

This code looks like it would work fine to me, but the rust borrow checker doesn't like it:
extern crate rustbox;
use std::error::Error;
use std::default::Default;
use rustbox::{Color, RustBox};
use rustbox::Key;
use std::fs::File;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
fn display_screenful(rb: &RustBox, fr: BufReader<&'static File>, offset: usize) {
for (rline, idx) in fr.lines().zip(0..).skip(offset).take(rb.height()) {
match rline {
Ok(line) => (*rb).print(0, idx, rustbox::RB_NORMAL, Color::White, Color::Black, &line),
Err(_) => (*rb).print(0, idx, rustbox::RB_NORMAL, Color::White, Color::Black, "")
}
}
}
fn main() {
let rustbox = match RustBox::init(Default::default()) {
Ok(v) => v,
Err(e) => panic!(e),
};
let path = env::args().nth(1).unwrap();
let file = match File::open(&path) {
Ok(file) => file,
Err(e) => panic!(e)
};
let file_reader = BufReader::new(&file);
display_screenful(&rustbox, file_reader, 0);
rustbox.present();
loop {
match rustbox.poll_event(false) {
Ok(rustbox::Event::KeyEvent(key)) => {
match key {
Some(Key::Char('q')) => { break; }
Some(Key::Char(' ')) => {
display_screenful(&rustbox, file_reader, rustbox.height());
rustbox.present();
}
_ => { }
}
},
Err(e) => panic!("{}", e.description()),
_ => { }
}
}
}
I guess I could not use a separate function, and use two for loop parts, instead, but that isn't idiomatic Rust, nor is it good coding practice. In fact, I've tried that, but it just tells me that I'm using a moved value. Here are some errors that I'm getting:
Compiling rusted v0.1.0 (file:///Users/christopherdumas/rusted)
src/main.rs:34:39: 34:43 error: `file` does not live long enough
src/main.rs:34 let file_reader = BufReader::new(&file);
^~~~
note: reference must be valid for the static lifetime...
src/main.rs:33:7: 55:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 33:6
src/main.rs:33 };
src/main.rs:34 let file_reader = BufReader::new(&file);
src/main.rs:35
src/main.rs:36 display_screenful(&rustbox, file_reader, 0);
src/main.rs:37 rustbox.present();
src/main.rs:38
...
src/main.rs:45:53: 45:64 error: use of moved value: `file_reader` [E0382]
src/main.rs:45 display_screenful(&rustbox, file_reader, rustbox.height());
^~~~~~~~~~~
src/main.rs:45:53: 45:64 help: run `rustc --explain E0382` to see a detailed explanation
src/main.rs:36:33: 36:44 note: `file_reader` moved here because it has type `std::io::buffered::BufReader<&'static std::fs::File>`, which is non-copyable
src/main.rs:36 display_screenful(&rustbox, file_reader, 0);
^~~~~~~~~~~
error: aborting due to 2 previous errors
Could not compile `rusted`.
To learn more, run the command again with --verbose.
Firstly, you shouldn't ask for a BufReader<&'static File>. You can't provide one. Ask instead for a BufReader<&'a File> for some lifetime 'a.
fn display_screenful<'a>(rb: &RustBox, fr: BufReader<&'a File>, offset: usize)
or, for short,
fn display_screenful(rb: &RustBox, fr: BufReader<&File>, offset: usize)
That's not enough either - you then end up moving file_reader into the function. You should borrow instead:
fn display_screenful(rb: &RustBox, fr: &mut BufReader<&File>, offset: usize)
and then it compiles.

Resources