passing external value to closure... error: capture of moved value - rust

I read a file given as an argument, but when I try to pass it to handle_client in a task at the bottom so that it can be written to the tcp stream when someone connects I get error: capture of moved value: html... what am I missing?
fn get_file_string(path_str: &String) -> String{
let path = Path::new(path_str.as_bytes());
let file = File::open(&path);
let mut reader = BufferedReader::new(file);
reader.read_to_string().unwrap()
}
fn main() {
let listener = TcpListener::bind("127.0.0.1:8001");
let mut acceptor = listener.listen();
let ref file_to_host = os::args()[1];
let html = get_file_string(file_to_host).clone();
fn handle_client(mut stream: TcpStream, html: String) {
let write = stream.write_str(html.as_slice());
}
for stream in acceptor.incoming() {
match stream {
Err(e) => { println!("{}", e) }
Ok(stream) => spawn(proc() {
handle_client(stream,html)
})
}
}
}

html is a String. Just like everything in Rust, a String is owned in exactly one place (if it satisfied Copy, it would be able to just duplicate it implicitly, but as it involves a heap allocation it’s definitely not). At present, you’re passing html to the handle_client function by value; therefore, when you call handle_client(stream, html), both stream and html are moved into that function and are no longer accessible. In the case of stream, that doesn’t matter as it’s a variable from inside the loop, but html comes from outside the loop; if it let you do it, it would take it the first time and work fine, but then it would be freed; second time through the loop, you would have an invalid String being passed through.
The solution in this case, seeing as you’re passing it through spawn and so can’t pass a reference (the slice, &str) is to clone the value so that that value can be moved into the proc and into the handle_client call:
Ok(stream) => {
let html = html.clone();
spawn(proc() {
handle_client(stream, html)
})
}

Related

Rust: How to fix borrowed value does not live long enough

I have simple client/server application. I am receiving message on the server side from client but I want to send that response to the channel from server to other file and I am receiving error "borrowed value does not live long enough".
I have searched in the stack overflow for similar previous questions but not getting enough understanding of lifetime. Is there a good documentation or if simple example available on this topic?
For now if someone can help me to fix this code (may be edit the portion of code which needs to fix) that would be helpful.
Thanks in advance.
Server side:
use std::os::unix::net::UnixDatagram;
use std::path::Path;
fn unlink_socket (path: impl AsRef<Path>) {
let path = path.as_ref();
if Path::new(path).exists() {
let result = std::fs::remove_file(path);
match result {
Err(e) => {
println!("Couldn't remove the file: {:?}", e);
},
_ => {}
}
}
}
pub fn tcp_datagram_server() {
pub static FILE_PATH: &'static str = "/tmp/datagram.sock";
let (tx, rx) = mpsc::channel();
let mut buf = vec![0; 1024];
unlink_socket(FILE_PATH);
let socket = match UnixDatagram::bind(FILE_PATH) {
Ok(socket) => socket,
Err(e) => {
println!("Couldn't bind: {:?}", e);
return;
}
};
println!("Waiting for client to connect...");
loop {
let received_bytes = socket.recv(buf.as_mut_slice()).expect("recv function failed");
println!("Received {:?}", received_bytes);
let received_message = from_utf8(buf.as_slice()).expect("utf-8 convert failed");
tx.clone().send(received_message);
}
}
fn main() {
tcp_datagram_server();
}
client side:
use std::sync::mpsc;
use std::os::unix::net::UnixDatagram;
use std::path::Path;
use std::io::prelude::*;
pub fn tcp_datagram_client() {
pub static FILE_PATH: &'static str = "/tmp/datagram.sock";
let socket = UnixDatagram::unbound().unwrap();
match socket.connect(FILE_PATH) {
Ok(socket) => socket,
Err(e) => {
println!("Couldn't connect: {:?}", e);
return;
}
};
println!("TCP client Connected to TCP Server {:?}", socket);
loop {
socket.send(b"Hello from client to server").expect("recv function failed");
}
}
fn main() {
tcp_datagram_client();
}
Error I am getting
error[E0597]: `buf` does not live long enough
--> src/unix_datagram_server.rs:38:42
|
38 | let received_message = from_utf8(buf.as_slice()).expect("utf-8 convert failed");
| ^^^ borrowed value does not live long enough
...
41 | }
| -
| |
| `buf` dropped here while still borrowed
| borrow might be used here, when `tx` is dropped and runs the `Drop` code for type `std::sync::mpsc::Sender`
|
= note: values in a scope are dropped in the opposite order they are defined
error: aborting due to previous error; 8 warnings emitted
For now if someone can help me to fix this code (may be edit the portion of code which needs to fix) that would be helpful.
Well the message seems rather clear. send does exactly what it says it does, it sends the parameter through the channel. This means the data must live long enough and remain valid "forever" (it needs to be alive and valid in the channel, as well as when fetched from it by the receiver).
That is not the case here. rustc can't understand that the function never returns, and it can panic anyway which will end up the same: the function will terminate, which will invalidate buf. Since received_message borrows buf, that means received_message can't be valid after the function has terminated. But at that point the message would still be in the channel waiting to be read (or retrieved by the receiver doing who knows what).
Therefore your construction is not allowed.
A second issue is that you're overwriting the buffer data on every loop, which has the same effect of breaking the message you sent during the previous iteration, and thus is not correct either. Though Rust won't let you do that either: if you work around the first error it will tell you that there's an outstanding shared borrow (the message sent through the channel) so you can't modify the backing buffer in the following iteration.
The solution is quite simple: have each iteration create an owned string (copying the current iteration's message) and send that through the channel:
tx.clone().send(received_message.to_string());
Also, these are more style / inefficiency remarks but:
The clone() on tx is completely redundant. The point of having a sender that is Clone is being able to send from multiple threads (hence mp in the channel name, that's for multiple producers). Here you have a single thread, the original sender works fine.
.as_slice() and .as_mut_slice() are rarely used unless necessary, which they aren't here: array references coerce to slices, so you can just use &mut buf and &buf. And why are you calling Path::new on something that's already a path? It doesn't do anything but it's not useful either.
It is rather annoying that your snippet is missing multiple imports and thus doesn't even compile as is.
From more of a unixy perspective, errors are usually printed on stderr. In Rust, eprintln does that for you (otherwise working in the same way println does). And I don't understand the purpose of marking a lexically nested static pub. Since the static is inside the function it's not even visible to the function's siblings, to say nothing of external callers. As a result I'd end up with this:
use std::os::unix::net::UnixDatagram;
use std::path::Path;
use std::sync::mpsc;
use std::str::from_utf8;
fn unlink_socket (path: impl AsRef<Path>) {
let path = path.as_ref();
if path.exists() {
if let Err(e) = std::fs::remove_file(path) {
eprintln!("Couldn't remove the file: {:?}", e);
}
}
}
static FILE_PATH: &'static str = "/tmp/datagram.sock";
pub fn tcp_datagram_server() {
unlink_socket(FILE_PATH);
let socket = match UnixDatagram::bind(FILE_PATH) {
Ok(socket) => socket,
Err(e) => {
eprintln!("Couldn't bind: {:?}", e);
return;
}
};
let (tx, _) = mpsc::channel();
let mut buf = vec![0; 1024];
println!("Waiting for client to connect...");
loop {
let received_bytes = socket.recv(&mut buf).expect("recv function failed");
println!("Received {:?}", received_bytes);
let received_message = from_utf8(&buf).expect("utf-8 convert failed");
tx.send(received_message.to_string());
}
}
There's a hint in the compiler message, that values in a scope are dropped in the opposite order they are defined in, and in the example, buf is defined after tx, which means it will be dropped before tx. Since a reference to buf (in the form of received_message) is passed to tx.send(), then buf should live longer that tx, and therefore switching the definition order will fix this particular error (ie. switch lines 19 and 20).

Calling async function inside of async function results in lifetime issues

The function run_av_check receives a vector filled with URLs which need to get called using reqwest. Iterating through the URLs gives me the error that the borrowed value does not live long enough. My understanding is that as long as check_availability is not finished for all URLs which are called in run_av_check using a for loop the run_av_check function is not finished, therefore the variable URLs should still exist during the runtime of all check_availibility calls.
I have read the Tokio and Rust documentation but I am not able to find a clue how and why this behaviour occurs. Why does the error pop up and what needs to be changed to make this work, both program wise and in my way of thinking?
async fn check_availability(host: &str) -> Result<reqwest::StatusCode, reqwest::Error> {
// ... building url
match client.get(&url).send().await {
Ok(r) => Ok(r.status()),
Err(_e) => Ok(reqwest::StatusCode::GATEWAY_TIMEOUT),
}
}
async fn run_av_check(urls: Vec<std::string::String>) -> Result<AvResponse, AvError> {
let mut av_urls: Vec<std::string::String> = vec![];
let mut s: std::string::String = "".to_string();
let mut attributes = Vec::new();
for url in urls.iter() {
attributes.push(("instance", url));
let resp = tokio::task::spawn(check_availability(url));
// ... processing result
let res = AvResponse {
av_urls,
prom_details: s,
};
Ok(res)
}
}
The problem is that you are starting independent tasks in tokio::task::spawn(check_availability(url));
This function takes only 'static references but your url refers to the local vector urls.
You need to make check_availability accept the String type (fn check_availability(host: String)) and call it like let resp = tokio::task::spawn(check_availability(url.to_string()));

Fill a string buffer from a thread [duplicate]

This question already has answers here:
How do I share a mutable object between threads using Arc?
(1 answer)
Lifetime troubles sharing references between threads
(1 answer)
Closed 5 years ago.
With hyper, I need to make an HTTP connection and read the results.
I want to wrap the whole thing in a timeout,
so I start a thread
and use recv_timeout to wait for it.
Wrapping just the send works,
but I want to also wrap read_to_string.
Here is the code:
fn send_request(url: &str) -> Result<Response, MyAppError> {
let mut c = Client::new();
let mut req = c.get(url);
req.send().map_err(|e| MyAppError::TcpError(e))
}
fn get_url(url: &str, mut buf: &mut String) -> Result<u16, MyAppError> {
let mut resp = send_request(url)?;
resp.read_to_string(&mut buf).map_err(|e| MyAppError::ReadError(e))?;
Ok(resp.status.to_u16())
}
fn get_url_with_timeout_2(url: &str, mut buf: &mut String) -> Result<u16, MyAppError> {
let (tx, rx) = mpsc::channel();
let url = url.to_owned();
let t = thread::spawn(move || {
match tx.send(get_url(&url, &mut buf)) {
Ok(()) => {} // everything good
Err(_) => {} // we have been released, no biggie
}
});
match rx.recv_timeout(Duration::from_millis(5000)) {
Ok(resp) => resp,
Err(_) => Err(MyAppError::Timeout),
}
}
Unfortunately I get a compiler error:
error[E0477]: the type `[closure#src/main.rs:53:25: 58:4 tx:std::sync::mpsc::Sender<std::result::Result<u16, MyAppError>>, url:std::string::String, buf:&mut std::string::String]` does not fulfill the required lifetime
--> src/main.rs:53:11
|
53 | let t = thread::spawn(move || {
| ^^^^^^^^^^^^^
|
= note: type must outlive the static lifetime
How can I pass the buffer to the thread,
let it fill it,
and then print out the buffer back on the main thread?
(This is Rust 1.15.1.)
This repository gives a complete main.rs and shows three examples for getting the webpage:
With no timeout.
With a timeout just on send.
With a timeout on the whole thing.
If you take out 3, it all compiles and runs.
What can I change about 3 to make that work too?
By the way, making a web request is really just the "occasion" for this question. I've already seen this question about doing a timeout. My own interest is not the timeout per se, but about how to fill up a buffer on one thread and read it on another.
Share mutable object between threads suggests using Arc and Mutex to safely share data between threads. Here is an attempt at that:
fn get_url_with_timeout_3(url: &str) -> Result<(u16, String), MyAppError> {
let (tx, rx) = mpsc::channel();
let url = url.to_owned();
let shbuf = Arc::new(Mutex::new(String::new()));
let shbuf2 = shbuf.clone();
let t = thread::spawn(move || {
let mut c = Client::new();
let mut req = c.get(&url);
let mut ret = match req.send() {
Ok(mut resp) => {
let mut buf2 = shbuf2.lock().unwrap();
match resp.read_to_string(&mut buf2) {
Ok(_) => Ok(resp.status.to_u16()),
Err(e) => Err(MyAppError::ReadError(e)),
}
}
Err(e) => Err(MyAppError::TcpError(e)),
};
match tx.send(ret) {
Ok(()) => {} // everything good
Err(_) => {} // we have been released, no biggie
}
});
match rx.recv_timeout(Duration::from_millis(5000)) {
Ok(maybe_status_code) => {
let buf2 = shbuf.lock().unwrap();
Ok((maybe_status_code?, *buf2))
}
Err(_) => Err(MyAppError::Timeout),
}
}
This gives me the error cannot move out of borrowed content for trying to return *buf2 (which makes sense, since it would be leaking data out of the Mutex), but I'm not sure how to express this in a structure where Rust can see the pattern.
If the request thread times out, it holds the lock forever — but I never try to read the buffer, so who cares. If the request thread finishes, it releases the lock and goes away, and the main thread will hold the only reference. I can reason that it is safe, but I'm not sure how to convince the compiler.
I'm not allowed to answer this question since it is supposedly a duplicate, but I've added 3 working implementations to my GitHub repository using the ideas from the comments.

If a Result returns Err(_), I want the whole function to return a HTTP request error

I'm trying to use the Iron framework to build a simple backend in Rust. This handler is just supposed to return the content of a certain file and I can get it to work properly with unwrap() but I want to try to do proper error handling. This is how I would imagine it would look like:
fn get_content(res: &mut Request) -> IronResult<Response> {
let mut id = String::new();
res.body.read_to_string(&mut id).unwrap();
let file_path_string = &("../content/".to_string() + &id + ".rdt");
// TODO: Error handling
match File::open(file_path_string) {
Ok(f) => {
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Err(err) => Err(Response::with(((status::InternalServerError), "File not found")))
};
}
This throws the error not all control paths return a value [E0269], which is fine. But if I add a response after the match part:
match File::open(file_path_string) {
Ok(f) => {
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Err(err) => Err(Response::with(((status::InternalServerError), "File not found")))
};
Err(Response::with(((status::InternalServerError), "File not found")))
I instead get the error message:
expected `iron::error::IronError`,
found `iron::response::Response`
(expected struct `iron::error::IronError`,
found struct `iron::response::Response`) [E0308]
src/main.rs:95
Err(Response::with(((status::InternalServerError), "File not found")))
I think the problem is the collision between Rust Err and Iron Err? I'm not sure though. And I have not done much web development (or Rust for that matter) in the past so any feedback on the code is also appreciated!
UPDATE: I think this is more "The Rust Way" to do it? But I'm not sure
fn get_content(res: &mut Request) -> IronResult<Response> {
let mut id = String::new();
res.body.read_to_string(&mut id).unwrap();
let file_path_string = &("../content/".to_string() + &id + ".rdt");
// TODO: Error handling
let f;
match File::open(file_path_string) {
Ok(file) => f = file,
Err(err) => Err(HttpError::Io(err))
};
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Having the code inside the error handling seems weird as read_to_string also needs to be taken care of and that would create a nested mess of error handling? However, these matching arms are obviously of incompatible types so it won't work... any suggestions?
An Ok() takes an Response, but an Err() takes an IronError.
Hence your call Err(...) is not valid when ... is a Response!
How to correct it? Well the first step is, you must create an IronError to send back. I believe (not familiar with Iron) that Iron will automatically an appropriate error code and that it's not your job to do that. In the documentation we find one key type implementing IronError:
pub enum HttpError {
Method,
Uri(ParseError),
Version,
Header,
TooLarge,
Status,
Io(Error),
Ssl(Box<Error + 'static + Send + Sync>),
Http2(HttpError),
Utf8(Utf8Error),
// some variants omitted
}
I can't see one which allows for an arbitrary string like "file not found". However, your use case is one of an IO failure, right? So it would make sense to use HttpError::Io with the std::IoError that you got back from File::open():
match File::open(file_path_string) {
Ok(f) => {
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Err(err) => Err(HttpError::Io(err))
};
By the way, it also fixes your "TODO: error handling"! How beautiful!
(Code untested, please feel free to edit if compilation fails)

How to resolve this Rust lifetime issue?

I am trying to read the contents of files in a directory in parallel. I'm running into lifetime issues.
My code looks like this:
use std::io::fs;
use std::io;
use std::collections::HashMap;
use std::comm;
use std::io::File;
fn main() {
let (tx, rx) = comm::channel(); // (Sender, Receiver)
let paths = fs::readdir(&Path::new("resources/tests")).unwrap();
for path in paths.iter() {
let task_tx = tx.clone();
spawn(proc() {
match File::open(path).read_to_end() {
Ok(data) => task_tx.send((path.filename_str().unwrap(), data)),
Err(e) => fail!("Could not read one of the files! Error: {}", e)
};
});
}
let mut results = HashMap::new();
for _ in range(0, paths.len()) {
let (filename, data) = rx.recv();
results.insert(filename, data);
}
println!("{}", results);
}
The compilation error I'm getting is:
error: paths does not live long enough
note: reference must be valid for the static lifetime...
note: ...but borrowed value is only valid for the block at 7:19
I also tried to use into_iter() (or move_iter() previously) in the loop without much success.
I'm suspecting it has to do with the spawned tasks remaining alive beyond the entire main() scope, but I don't know how I can fix this situation.
The error message might be a bit confusing but what it's telling you is that you are trying to use a reference path inside of a task.
Because spawn is using proc you can only use data that you can transfer ownership of to that task (Send kind).
To solve that you can do this (you could use a move_iter but then you can't access paths after the loop):
for path in paths.iter() {
let task_tx = tx.clone();
let p = path.clone();
spawn(proc() {
match File::open(&p).read_to_end() {
The second problem is that you are trying to send &str (filename) over a channel. Same as for tasks types used must be of kind Send:
match File::open(&p).read_to_end() {
Ok(data) => task_tx.send((p.filename_str().unwrap().to_string(), data)),
Err(e) => fail!("Could not read one of the files! Error: {}", e)
};

Resources