Fill a string buffer from a thread [duplicate] - rust

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.

Related

deno_runtime running multiple invokes on single worker concurrently

I'm trying to run multiple invocation of the same script on a single deno MainWorker concurrently, and waiting for their
results (since the scripts can be async). Conceptually, I want something like the loop in run_worker below.
type Tx = Sender<(String, Sender<String>)>;
type Rx = Receiver<(String, Sender<String>)>;
struct Runner {
worker: MainWorker,
futures: FuturesUnordered<Pin<Box<dyn Future<Output=(String, Result<Global<Value>, Error>)>>>>,
response_futures: FuturesUnordered<Pin<Box<dyn Future<Output=(String, Result<(), SendError<String>>)>>>>,
result_senders: HashMap<String, Sender<String>>,
}
impl Runner {
fn new() ...
async fn run_worker(&mut self, rx: &mut Rx, main_module: ModuleSpecifier, user_module: ModuleSpecifier) {
self.worker.execute_main_module(&main_module).await.unwrap();
self.worker.preload_side_module(&user_module).await.unwrap();
loop {
tokio::select! {
msg = rx.recv() => {
if let Some((id, sender)) = msg {
let global = self.worker.js_runtime.execute_script("test", "mod.entry()").unwrap();
self.result_senders.insert(id, sender);
self.futures.push(Box::pin(async {
let resolved = self.worker.js_runtime.resolve_value(global).await;
return (id, resolved);
}));
}
},
script_result = self.futures.next() => {
if let Some((id, out)) = script_result {
self.response_futures.push(Box::pin(async {
let value = deserialize_value(out.unwrap(), &mut self.worker);
let res = self.result_senders.remove(&id).unwrap().send(value).await;
return (id.clone(), res);
}));
}
},
// also handle response_futures here
else => break,
}
}
}
}
The worker can't be borrowed as mutable multiple times, so this won't work. So the worker has to be a RefCell, and
I've created a BorrowingFuture:
struct BorrowingFuture {
worker: RefCell<MainWorker>,
global: Global<Value>,
id: String
}
And its poll implementation:
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut Box::pin(self.worker.borrow_mut().js_runtime.resolve_value(self.global.clone()))).poll(cx) {
Poll::Ready(result) => Poll::Ready((self.id.clone(), result)),
Poll::Pending => {
cx.waker().clone().wake_by_ref();
Poll::Pending
}
}
}
So the above
self.futures.push(Box::pin(async {
let resolved = self.worker.js_runtime.resolve_value(global).await;
return (id, resolved);
}));
would become
self.futures.push(Box::pin(BorrowingFuture{worker: self.worker, global: global.clone(), id: id.clone()}));
and this would have to be done for the response_futures above as well.
But I see a few issues with this.
Creating a new future on every poll and then polling that seems wrong, but it does work.
It probably has a performance impact because new objects are created constantly.
The same issue would happen for the response futures, which would call send on each poll, which seems completely wrong.
The waker.wake_by_ref is called on every poll, because there is no way to know when a script result
will resolve. This results in the future being polled thousands (and more) times per second (always creating a new object),
which could be the same as checking it in a loop, I guess.
Note My current setup doesn't use select!, but an enum as Output from multiple Future implementations, pushed into
a single FuturesUnordered, and then matched to handle the correct type (script, send, receive). I used select here because
it's far less verbose, and gets the point across.
Is there a way to do this better/more efficiently? Or is it just not the way MainWorker was meant to be used?
main for completeness:
#[tokio::main]
async fn main() {
let main_module = deno_runtime::deno_core::resolve_url(MAIN_MODULE_SPECIFIER).unwrap();
let user_module = deno_runtime::deno_core::resolve_url(USER_MODULE_SPECIFIER).unwrap();
let (tx, mut rx) = channel(1);
let (result_tx, mut result_rx) = channel(1);
let handle = thread::spawn(move || {
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
let mut runner = Runner::new();
runtime.block_on(runner.run_worker(&mut rx, main_module, user_module));
});
tx.send(("test input".to_string(), result_tx)).await.unwrap();
let result = result_rx.recv().await.unwrap();
println!("result from worker {}", result);
handle.join().unwrap();
}

How do you pass a struct that contains a String reference between threads?

In the following code snippet I am trying to create a tuple that contains a String value and a struct that contains an attribute that is set as the reference to the first value (which would be the String) in the tuple.
My question is how do I make the String (which is the first value in the tuple) live long enough.
Here is the code:
#[derive(Debug, Clone)]
struct RefTestStruct {
key: usize,
ref_value: Option<&'static str>,
}
fn init3<'a>( cache: &Arc<Mutex<HashMap<usize, (String, Option<RefTestStruct>)>>> ) {
let mut handles: Vec<JoinHandle<()>> = vec![];
for idx in 0..10 {
//create reference copy of cache
let cache_clone = Arc::clone(cache);
let handle = thread::spawn(move || {
//lock cache
let mut locked_cache = cache_clone.lock().unwrap();
// add new object to cache
let tuple_value = (format!("value: {}", idx), None );
let mut ts_obj =
RefTestStruct {
key: idx,
ref_value: Some( tuple_value.0.as_str() ),
};
tuple_value.1 = Some(ts_obj);
locked_cache.insert(idx as usize, tuple_value);
println!("IDX: {} - CACHE: {:?}", idx, locked_cache.get( &(idx as usize) ).unwrap() )
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("\n");
}
fn main() {
// init
let cache = HashMap::<usize, (String, Option<RefTestStruct>)>::new();
let am_cache = Arc::new(Mutex::new(cache));
init3(&am_cache);
// change cache contents
let mut handles: Vec<JoinHandle<()>> = vec![];
for idx in 0..10 {
let cache_clone = Arc::clone(&am_cache);
let handle = thread::spawn(move || {
let mut locked_cache = cache_clone.lock().unwrap();
let tuple_value = locked_cache.get_mut( &(idx as usize) ).unwrap();
(*tuple_value).0 = format!("changed value: {}", (*tuple_value).1.unwrap().key + 11);
let ts_obj =
RefTestStruct {
key: (*tuple_value).1.unwrap().key,
ref_value: Some( (*tuple_value).0.as_str() ),
};
tuple_value.1 = Some(ts_obj);
// locked_cache.insert(idx as usize, tuple_value);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
// display cache contents
let mut handles: Vec<JoinHandle<()>> = vec![];
for idx in 0..10 {
let cache_clone = Arc::clone(&am_cache);
let handle = thread::spawn(move || {
let locked_cache = cache_clone.lock().unwrap();
let ts_obj = locked_cache.get( &(idx as usize) ).unwrap();
println!("IDX: {} - CACHE: {:?}", idx, &ts_obj );
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
ERROR:
error[E0597]: `tuple_value.0` does not live long enough
--> src\main.rs:215:42
|
215 | ref_value: Some( tuple_value.0.as_str() ),
| ^^^^^^^^^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `tuple_value.0` is borrowed for `'static`
...
221 | });
| - `tuple_value.0` dropped here while still borrowed
How do you pass a struct that contains a String reference between threads?
First I answer the question in the caption and ignore the program you posted (And I'll try to ignore my itch to guess what you really want to do.)
I'll assume, you want to avoid unsafe in your own code. (So dependencies with unsafe code might be ok.)
To pass a reference to a thread, the reference has to live as long as the thread will live.
Your options:
Have a 'static lifetime on your reference.
Use threads which have a non 'static lifetime.
Regarding 1. have a 'static lifetime on the reference.
So now the questions changed into, how to obtain a reference to
a String with 'static lifetime.
We can't use a constant, as we can't initialize it.
This leaves us with the possibility to Box the String and leak it.
But of course, this leaks memory, as the String can't be dropped any more. Also you get a &'mut String out
of it, so only one thread can have it at a time and after being
done with it, pass it on. So to me it looks like it is essentially
the same you could do with the owned value of type String too,
so why bother and risk the memory leak. (Putting a Arc<Mutex<..>> around it, is also "same you coud do with the owned value".).
Other methods might exist, which I am not aware of.
Regarding 2. Use threads which have a non `'static' lifetime.
These are called scoped threads and guarantee, that the thread exits,
before the lifetime ends. These are not in standard rust yet, but there
are crates implementing those (I guess using unsafe).
For example crossbeam offers an implementation of this.
Looking at your program though, I guess you want to have long lived
threads.
The program you posted and what you probably want to achieve.
The program you posted has a different issue besides.
You try to create a self referential value in your cache. As far as I know, this is not possible in rust, without using a shared ownership tool, like Arc.
If you try to use a plain Arc, the access would be read only and users of the cache could only change, which String the cache entry points to, but not the "pointed to" String itself.
So If one user replaced the Arc<String> in the cache, other threads would still have a reference to the "old" String and don't see the changed value. But I guess that is what you want to achieve.
But that is a conceptual problem, you can't have unsynchronized read access to a value, which can then be mutated concurrently (by whatever means).
So, if you want to have the ability to change the value with all prior users being able to see it, this leaves you with Mutex or RWLock depending on the access pattern, you are anticipating.
A solution using Arc<String> with the aforementioned defects:
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};
#[derive(Debug, Clone)]
struct RefTestStruct {
key: usize,
_ref_value: Arc<String>,
}
type Cache = HashMap<usize, (Arc<String>, RefTestStruct)>;
type AmCache = Arc<Mutex<Cache>>;
fn init3(cache: &AmCache) {
let mut handles: Vec<JoinHandle<()>> = vec![];
for idx in 0..10_usize {
//create reference copy of cache
let cache_clone = Arc::clone(cache);
let handle = thread::spawn(move || {
//lock cache
let mut locked_cache = cache_clone.lock().unwrap();
// add new object to cache
let s = Arc::new(format!("value: {}", idx));
let ref_struct = RefTestStruct {
key: idx,
_ref_value: s.clone(),
};
let tuple_value = (s, ref_struct);
locked_cache.insert(idx, tuple_value);
println!(
"IDX: {} - CACHE: {:?}",
idx,
locked_cache.get(&idx).unwrap()
)
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("\n");
}
fn main() {
// init
let cache = Cache::new();
let am_cache = Arc::new(Mutex::new(cache));
init3(&am_cache);
// change cache contents
let mut handles: Vec<JoinHandle<()>> = vec![];
for idx in 0..10_usize {
let cache_clone = Arc::clone(&am_cache);
let handle = thread::spawn(move || {
let mut locked_cache = cache_clone.lock().unwrap();
let tuple_value = locked_cache.get_mut(&idx).unwrap();
let new_key = tuple_value.1.key + 11;
let new_s = Arc::new(format!("changed value: {}", new_key));
(*tuple_value).1 = RefTestStruct {
key: new_key,
_ref_value: new_s.clone(),
};
(*tuple_value).0 = new_s;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
// display cache contents
let mut handles: Vec<JoinHandle<()>> = vec![];
for idx in 0..10_usize {
let cache_clone = Arc::clone(&am_cache);
let handle = thread::spawn(move || {
let locked_cache = cache_clone.lock().unwrap();
let ts_obj = locked_cache.get(&idx).unwrap();
println!("IDX: {} - CACHE: {:?}", idx, &ts_obj);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
I am sure it is not difficult to derive the Arc<Mutex<String>> version from that, if desired.
That would not be as pointless, as one might think. The Mutex around the cache protects the HashMap for insertion and deletion. And the Mutex around the String values, protects the string values themselves and can be locked indepently of the Mutex around the HashMap. Usual caveats apply, if you ever need both locks at the same time, lock them in the same order always, or risk a deadlock.

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

rust - Send data from function argument (from a thread) into mpsc channel

I'm trying to handle TcpStream using threads in a Rust program (a tcp server). I want to keep tracks of currents clients connections in a HashMap using an Arc<Mutex<HashMap>>.
When a thread is finished, to remove the connection from the HashMap, I thought about using an uuid, passed to the function used to handle the stream. Like this it could be send inside a mpsc channel to the main thread and then remove the connection from the HashMap.
The code from the main thread :
let clients_streams = Arc::new(Mutex::new(HashMap::new()));
// Server thread. Create channel
let (server_sender, server_receiver) = mpsc::channel();
loop {
for stream in server_socket.incoming() {
let clients_streams = Arc::clone(&clients_streams);
let server_sender = server_sender.clone();
let mut cs = clients_streams.lock().unwrap();
match stream {
Ok(stream) => {
let mut cs = clients_streams.lock().unwrap();
let uuid = Uuid::new_v4().to_string();
cs.insert(uuid.clone(), stream.try_clone());
thread::spawn(move || {
handle_client(stream, server_sender.clone(), uuid.clone());
})
}
Err(err) => thread::spawn(move || {
println!("Connection failed : {}", err);
}),
};
}
The handle_client function :
fn handle_client<'a>(stream: TcpStream, sender: mpsc::Sender<&'a [u8]>, uuid: String) {
let mut reader = BufReader::new(&stream);
let mut writer = BufWriter::new(&stream);
writer.write(b"Hello\n").unwrap();
writer.flush().unwrap();
// Read from client
loop {
let mut resp = Vec::new();
let read_bytes = reader.read_until(b'\n', &mut resp);
match read_bytes {
Ok(read_bytes) => {
if read_bytes == 0 {
let msg = format!("end of {}", uuid);
println!("connection closed by remote");
sender.send(msg.as_bytes()).unwrap();
break;
};
}
Err(err) => match err.kind() {
io::ErrorKind::Interrupted => continue,
_ => break,
},
}
}
}
This code doesn't compile, it shows this error but I don't understand how to resolve it. The variable msg doesn't live long enough, because of the uuid inside of it, but why ? I had to add the lifetime because I'm sending uuid inside the channel.
fn handle_client<'a>(stream: TcpStream, sender: mpsc::Sender<&'a [u8]>, uuid: String) {
-- lifetime `'a` defined here
sender.send(msg.as_bytes()).unwrap();
------------^^^------------
| |
| borrowed value does not live long enough
argument requires that `msg` is borrowed for `'a`
break;
};
- `msg` dropped here while still borrowed
Uuid does not affect msg because format macro creates a clean new String. You are trying to send a &[u8] (this is what as_bytes() return). You need to find a way to remove this reference. (maybe this something like How to convert from &[u8] to Vec<u8>?) You can also share references with Rc.
PS: this more a comment than an answer but I can't post answers

Resources