How to pass an object by reference inside Rust Warp callback? - rust

I am pretty new to Rust programming and still trying to understand the concepts around memory and ownership.
I have this code.
#[tokio::main]
async fn main() {
let file_root = String::from("test_dir/");
let content_dir = String::from("posts_");
let obj = msgnode::MsgNode::new(file_root.clone(), content_dir.clone());
let node_filter = warp::any().map(move || obj.clone());
// GET /hello/warp => 200 OK with body "Hello, warp!"
let hello = warp::get()
.and(warp::path("waservice"))
.and(warp::path::end())
.and(msg_body())
.and(node_filter.clone())
.and_then(store_wamsg);
warp::serve(hello)
.run(([127, 0, 0, 1], 3030))
.await;
}
mod msgnode;
Everything is fine with that, except that I cannot work with cloned instance of MsgNode object. Inside the store_wamsg I am using the MsgNode object as following:
async fn store_wamsg(item: MsgItem, node : &msgnode::MsgNode) -> Result<impl warp::Reply, warp::Rejection> {
let mut node2 = node.clone();
node2.create_file(item.msg_content);
Ok(warp::reply::with_status(
"All good",
http::StatusCode::OK,
))
}
My question is, if there is a way that I don't need to use multiple cloned instances of MsgNode object inside the main function, every time a new request is made to the service?
To be more precise I want to do something like this:
let node_filter = warp::any().map(move || &obj);
And somehow pass the reference inside the store_wamsg function. Right now when I do this I run into the following error:
116 | let node_filter = warp::any().map(move || &obj);
| ------- ^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 MsgNode
| lifetime `'1` represents this closure's body
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure

You can enacapsulate msgnode in Arc Mutex to sync access:
use std::sync::{Arc, Mutex};
let obj = Arc::new(Mutex::new(msgnode::MsgNode::new(
file_root.clone(),
content_dir.clone(),
)));
let node_filter = warp::any().map(move || obj.clone());
Here, obj.clone() clones the atomic resouce counter, not msgnode. So that there is only one instance of MsgNode and threads coordinate their access to it with the help of Arc and Mutex.

Related

Proper way to share references to Vec between threads

I am new to rust and I am attempting to create a Vec that will live on the main thread, and pass a reference to another thread, which then pushes members onto the vector, for the main thread to use.
use std::{thread};
fn main() {
let mut v: Vec<u8> = Vec::new();
let _ = thread::spawn(move || {
vec_push(&mut v, 0)
});
for i in v.iter_mut() {
println!("poo {}", i);
}
}
fn vec_push(v: &mut Vec<u8>, n: u8) {
v.push(n);
}
This is a simplified version of what I am trying to do. In my main code I am want it to be a Vec of TcpStreams.
I think this post would also apply to maintaining a struct (that doesn't implement Copy) between threads.
I get this error
error[E0382]: borrow of moved value: `v`
--> src/main.rs:8:11
|
4 | let mut v: Vec<u8> = Vec::new();
| ----- move occurs because `v` has type `Vec<u8>`, which does not implement the `Copy` trait
5 | let _ = thread::spawn(move || {
| ------- value moved into closure here
6 | vec_push(&mut v, 0)
| - variable moved due to use in closure
7 | });
8 | for i in v.iter_mut() {
| ^^^^^^^^^^^^ value borrowed here after move
Is there a better way to do this? Am I missing some basic concept?
Any help would be useful, I am used to C where I can just throw around references willy-nilly
What you are doing is wildly unsound. You are trying to have two mutable references to a object, which is strictly forbidden in rust. Rust forbids this to prevent you from having data races that would result in memory unsafety.
If you want to mutate an object from different threads you have to synchronize it somehow. The easiest way to do it is by using Mutex. This probably won't be very efficient in a high-congestion scenario (as locking a mutex can become your bottle neck), but it will be safe.
To share this Mutex between threads you can wrap it in an Arc (an atomic counted shared reference smart pointer). So your code can be transformed to something like this:
use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let v = Arc::new(Mutex::new(Vec::new()));
let v_clone = Arc::clone(&v);
let t = thread::spawn(move || {
vec_push(v_clone, 0)
});
t.join().unwrap();
for i in v.lock().unwrap().iter_mut() {
println!("poo {}", i);
}
}
fn vec_push(v: Arc<Mutex<Vec<u8>>>, n: u8) {
v.lock().unwrap().push(n);
}
You probably will also want to join your spawned thread, so you should name it.

How to use FileReader in rust wasm?

This is part of my code, the error in rest move |event: Event|
pub struct FileStream{
el : HtmlInputElement,
}
impl FileStream {
pub fn new(el : HtmlInputElement) -> FileStream {
FileStream {el}
}
pub fn get_file(&self){
let file = self.el.files().unwrap().get(0).unwrap();
let file_reader = FileReader::new().unwrap();
file_reader.read_as_data_url(&file).unwrap();
let onloadend = Closure::wrap(Box::new(move |event: Event| {
let file_reader = file_reader.unchecked_into::<web_sys::FileReader>();
let data_url = file_reader.result().unwrap();
let data_url = data_url.unchecked_into::<JsValue>();
let data_url = data_url.as_string().unwrap();
let audio_el = self.el.unchecked_into::<HtmlAudioElement>();
audio_el.set_src(&data_url);
}) as Box<dyn FnMut(_)>);
file_reader.set_onloadend(Some(onloadend.as_ref().unchecked_ref()));
}
}
I don't know how to fix this error...
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/lib.rs:29:48
|
29 | let onloadend = Closure::wrap(Box::new(move |event: Event| {
| - ^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
| _______________________________________|
| |
30 | | let file_reader = file_reader.unchecked_into::<web_sys::FileReader>();
| | ----------- closure is `FnOnce` because it moves the variable `file_reader` out of its environment
31 | | let data_url = file_reader.result().unwrap();
32 | | let data_url = data_url.unchecked_into::<JsValue>();
... |
35 | | audio_el.set_src(&data_url);
36 | | }) as Box<dyn FnMut(_)>);
| |__________- the requirement to implement `FnMut` derives from here
For more information about this error, try `rustc --explain E0525`.
When the closure is created, it moves file_reader into itself (because it is declared as a move || closure). Then, when the closure is called, it consumes file_reader via calling unchecked_into(). Therefore, if the closure were to be called a second time, it would no longer have a file_reader to use. That's why the compiler decides the closure is only a “FnOnce”.
On the other hand, the set_onloadend callback mechanism wants a function that can be called more than once, in case the underlying event somehow happens more than once.
The general solution to this problem is to adjust your function so that it can be called more than once — as far as the compiler is concerned. You can do this by putting the value that must be moved in an Option outside the closure, then using Option::take() inside the closure to get it back out — thus leaving the Option value to be None if the function ever gets called again.
However, in this case, it would be simpler to just remove the entire unchecked_into() line. As far as I can see, there is no need for it, because the object is already typed as a FileReader. (But I haven't tried compiling your code to see if it works without, so maybe I missed something.)

Life-time error in Parallel segmented-prime-sieve in Rust

I am trying to make parallel a prime sieve in Rust but Rust compiler not leave of give me a lifemetime error with the parameter true_block.
And data races are irrelevant because how primes are defined.
The error is:
error[E0621]: explicit lifetime required in the type of `true_block`
--> src/sieve.rs:65:22
|
50 | true_block: &mut Vec<bool>,
| -------------- help: add explicit lifetime `'static` to the type of `true_block`: `&'static mut Vec<bool>`
...
65 | handles.push(thread::spawn(move || {
| ^^^^^^^^^^^^^ lifetime `'static` required
The code is:
fn extend_helper(
primes: &Vec<usize>,
true_block: &mut Vec<bool>,
segment_min: usize,
segment_len: usize,
) {
let mut handles: Vec<thread::JoinHandle<()>> = vec![];
let arc_primes = Arc::new(true_block);
let segment_min = Arc::new(segment_min);
let segment_len = Arc::new(segment_len);
for prime in primes {
let prime = Arc::new(prime.clone());
let segment_min = Arc::clone(&segment_min);
let segment_len = Arc::clone(&segment_len);
let shared = Arc::clone(&arc_primes);
handles.push(thread::spawn(move || {
let tmp = smallest_multiple_of_n_geq_m(*prime, *segment_min) - *segment_min;
for j in (tmp..*segment_len).step_by(*prime) {
shared[j] = false;
}
}));
}
for handle in handles {
handle.join().unwrap();
}
}
The lifetime issue is with true_block which is a mutable reference passed into the function. Its lifetime is only as long as the call to the function, but because you're spawning a thread, the thread could live past the end of the function (the compiler can't be sure). Since it's not known how long the thread will live for, the lifetime of data passed to it must be 'static, meaning it will live until the end of the program.
To fix this, you can use .to_owned() to clone the array stored in the Arc, which will get around the lifetime issue. You would then need to copy the array date out of the Arc after joining the threads and write it back to the true_block reference. But it's also not possible to mutate an Arc, which only holds an immutable reference to the value. You need to use an Arc<Mutex<_>> for arc_primes and then you can mutate shared with shared.lock().unwrap()[j] = false; or something safer

Why am I able to call a closure twice even though I have moved a variable into it?

fn main() {
let mut a = String::from("dd");
let mut x = move || {
a.push_str("string: &str");
};
x();
x();
}
I have added move here to capture a but I am still able to call the x closure twice. Is a still borrowed as a mutable reference here? Why doesn't move force a move?
The variable a has indeed been moved into the closure:
fn main() {
let mut a = String::from("dd");
let mut x = move || {
a.push_str("string: &str");
};
x();
x();
a.len();
}
error[E0382]: borrow of moved value: `a`
--> src/main.rs:9:5
|
2 | let mut a = String::from("dd");
| ----- move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait
3 | let mut x = move || {
| ------- value moved into closure here
4 | a.push_str("string: &str");
| - variable moved due to use in closure
...
9 | a.len();
| ^ value borrowed here after move
It's unclear why you think that the closure x would become invalid after calling it, but it doesn't. No more than the same applied to a struct:
struct ClosureLike {
a: String,
}
impl ClosureLike {
fn call(&mut self) {
self.a.push_str("string: &str");
}
}
fn main() {
let a = String::from("dd");
let mut x = ClosureLike { a };
x.call();
x.call();
}
The question came from my wrong understanding of closures. The way it is documented in the Rust book also contributed to the confusion (I am not saying the book is bad). If anyone else had this same confusion, here is what I found.
Closures do not just store the scope and run it when its called. It captures the environment in the preferred way. The environment which contains a is stored in the closure. How the values are captured from the environment decides the trait.
The value of a persists until the closure exists, unless some operation moves it, such as if the closure returns a or a method consumes a. Here, nothing moves a out of the closure so the closure can be called as many times as I want.
A better understanding can be obtained from the FnOnce, FnMut, and Fn traits. These traits are decided by how the variables are captured by the closure, not by how the variables are moved into the closure. FnMut can be implemented on a closure where a value is moved .

Nested closure borrow failed

I want to parse a YAML file and use the value inside a service for a HTTP request. Line 35 is the end of main function.
extern crate hyper;
extern crate libc;
extern crate yaml_rust;
use hyper::rt::Future;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use std::sync::Arc;
use yaml_rust::YamlLoader;
fn main() {
let content: String = String::from("response: Hello world");
let cfg = Arc::new(YamlLoader::load_from_str(content.as_str()).unwrap());
let cfg0 = (&cfg[0]).clone();
let cfg_response = (&cfg0)["response"].as_str().unwrap();
// A `Service` is needed for every connection, so this
// creates on of our `hello_world` function.
let handle = move || {
let cfg_response = cfg_response.clone();
service_fn_ok(move |_| Response::new(Body::from(String::from(cfg_response.clone()))))
};
// Serve HTTP protocol
// This is our socket address...
let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr)
.serve(handle)
.map_err(|e| eprintln!("server error: {}", e));
// Run this server for... forever!
hyper::rt::run(server);
}
Unfortunately, I encountered a nested closure leading to a strange borrow error:
error[E0597]: `cfg0` does not live long enough
--> src/main.rs:15:26
|
15 | let cfg_response = (&cfg0)["response"].as_str().unwrap();
| ^^^^ borrowed value does not live long enough
...
35 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
I tried to
clone it before borrow
use Arc to make it counter-based,
modify assignment
all to no avail
Why does this happen? How do I solve this?
The functions you pass the closure into - hyper::server::Builder::serve and hyper::rt::run() - require their arguments to be 'static, rather than being bounded by any function. main isn't considered special in this regard.
The value bounding it, cfg_response, is captured by the outer closure, so the nested closure isn't necessary to get the error.
Here's a very small program that has the same problem:
fn main() {
let cfg0 = String::from("hello world");
let cfg_response: &str = &cfg0;
let handle = move || {
// this closure takes ownership of cfg_response, a reference to cfg0. Since cfg0 will not
// outlive the function, neither can handle. If it instead took ownership of cfg0 or a
// clone of it, it would have no outside references and could live forever.
return cfg_response.to_owned();
};
serve(handle);
}
fn serve<F: Fn() -> String + 'static>(handle: F) {
loop {
println!("{}", handle());
}
}
As #Stargateur pointed out, this can be solved by making cfg_response owned.
Alternatively, you could initialize cfg0 in a lazy_static like so:
#[macro_use]
extern crate lazy_static;
lazy_static! {
static ref cfg0: String = String::from("hello world");
}
This way, you can still use a borrowed value because it meets the lifetime requirements.

Resources