Issue with borrowing while spawning thread inside other thread - multithreading

I have read the following question:
How can I run a set of functions on a recurring interval without running the same function at the same time using only the standard Rust library?
and elaborated some more complex tests.
The following code add a &str parameter for the functions and it works:
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let scheduler = thread::spawn(|| {
let wait_time = Duration::from_millis(500);
let one: &str = "Alpha";
let two: &str = "Beta";
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let thread_a = thread::spawn(move || { a(&one) });
let thread_b = thread::spawn(move || { b(&two) });
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
});
scheduler.join().expect("Scheduler panicked");
}
fn a(a: &str) {
eprintln!("{}", a);
thread::sleep(Duration::from_millis(100))
}
fn b(b: &str) {
eprintln!("{}", b);
thread::sleep(Duration::from_millis(200))
}
My understanding is that this works because Copy Trait is implemented for str.
Now consider the following example:
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let scheduler = thread::spawn(|| {
let wait_time = Duration::from_millis(500);
let one: String = String::from("Alpha");
let two: String = String::from("Beta");
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let thread_a = thread::spawn(move || { a(&one) });
let thread_b = thread::spawn(move || { b(&two) });
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
});
scheduler.join().expect("Scheduler panicked");
}
fn a(a: &str) {
eprintln!("{}", a);
thread::sleep(Duration::from_millis(100))
}
fn b(b: &str) {
eprintln!("{}", b);
thread::sleep(Duration::from_millis(200))
}
I am getting this at compile time:
error[E0382]: use of moved value: `one`
--> src\main.rs:19:42
|
10 | let one: String = String::from("Alpha");
| --- move occurs because `one` has type `String`, which does not implement the `Copy` trait
...
19 | let thread_a = thread::spawn(move || { a(&one) });
| ^^^^^^^ --- use occurs due to use in closure
| |
| value moved into closure here, in previous iteration of loop
error[E0382]: use of moved value: `two`
--> src\main.rs:20:42
|
11 | let two: String = String::from("Beta");
| --- move occurs because `two` has type `String`, which does not implement the `Copy` trait
...
20 | let thread_b = thread::spawn(move || { b(&two) });
| ^^^^^^^ --- use occurs due to use in closure
| |
| value moved into closure here, in previous iteration of loop
error: aborting due to 2 previous errors
EDIT1
It seems it can be solved with the use of .clone()
But now consider the following code:
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let one: String = String::from("Alpha");
let two: String = String::from("Beta");
let scheduler = thread::spawn(|| {
let wait_time = Duration::from_millis(500);
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let one = one.clone();
let two = two.clone();
let thread_a = thread::spawn(move || { a(&one) });
let thread_b = thread::spawn(move || { b(&two) });
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
});
scheduler.join().expect("Scheduler panicked");
}
fn a(a: &str) {
eprintln!("{}", a);
thread::sleep(Duration::from_millis(100))
}
fn b(b: &str) {
eprintln!("{}", b);
thread::sleep(Duration::from_millis(200))
}
I am now getting different error codes:
error[E0373]: closure may outlive the current function, but it borrows `two`, which is owned by the current function
--> src\main.rs:11:35
|
11 | let scheduler = thread::spawn(|| {
| ^^ may outlive borrowed value `two`
...
21 | let two = two.clone();
| --- `two` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src\main.rs:11:21
|
11 | let scheduler = thread::spawn(|| {
| _____________________^
12 | | let wait_time = Duration::from_millis(500);
13 | |
14 | | // Make this an infinite loop
... |
38 | | }
39 | | });
| |______^
help: to force the closure to take ownership of `two` (and any other referenced variables), use the `move` keyword
|
11 | let scheduler = thread::spawn(move || {
| ^^^^^^^
error[E0373]: closure may outlive the current function, but it borrows `one`, which is owned by the current function
--> src\main.rs:11:35
|
11 | let scheduler = thread::spawn(|| {
| ^^ may outlive borrowed value `one`
...
20 | let one = one.clone();
| --- `one` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src\main.rs:11:21
|
11 | let scheduler = thread::spawn(|| {
| _____________________^
12 | | let wait_time = Duration::from_millis(500);
13 | |
14 | | // Make this an infinite loop
... |
38 | | }
39 | | });
| |______^
help: to force the closure to take ownership of `one` (and any other referenced variables), use the `move` keyword
|
11 | let scheduler = thread::spawn(move || {
| ^^^^^^^
error: aborting due to 2 previous errorsù

For brevity I'll only mention one, but the same applies to two. The issue with thread::spawn(move || { a(&one) }) is that one is moved into the closure, which then results in the compile error, as one is no longer available for the next iteration.
Borrowing &one up front won't work either, because the thread borrowing one can outlive the outer thread. To get it working you can clone one (and two) before spawning the threads.
let one = one.clone();
let two = two.clone();
let thread_a = thread::spawn(move || a(&one));
let thread_b = thread::spawn(move || b(&two));
Alternatively, if you really want to borrow it, and not clone it. Then you can use, e.g. crossbeam and a scope for spawning threads. See also "How can I pass a reference to a stack variable to a thread?".
...
let one: String = String::from("Alpha");
let two: String = String::from("Beta");
let one = &one;
let two = &two;
crossbeam::scope(|scope| {
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let thread_a = scope.spawn(move |_| a(&one));
let thread_b = scope.spawn(move |_| b(&two));
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
})
.unwrap();

If you're sharing non-static immutable data among multiple threads, use Arc. That's what it's for.

Related

How do I change the structure in the thread?

Please help me, I'm completely confused.
How do I make this code work?
I need to change the structure members in the thread...
#[derive(Debug)]
struct S {
str: String,
b: bool,
i: u128,
}
fn main() {
let mut vec_s = vec![];
vec_s.push(S {
str: "a".to_string(),
b: false,
i: 0,
});
let mut threads = vec![];
for s in vec_s {
{
let mut _s = &s;
threads.push(std::thread::spawn(move || {
_s.b = true;
_s.str = "b".to_string();
_s.i = 1;
}));
}
}
for thread in threads {
let _ = thread.join();
}
dbg!(&vec_s);
}
The compiler outputs a lot of errors:
error[E0594]: cannot assign to `_s.b`, which is behind a `&` reference
--> src/main.rs:23:17
|
23 | _s.b = true;
| ^^^^^^^^^^^ cannot assign
error[E0594]: cannot assign to `_s.str`, which is behind a `&` reference
--> src/main.rs:24:17
|
24 | _s.str = "b".to_string();
| ^^^^^^ cannot assign
error[E0594]: cannot assign to `_s.i`, which is behind a `&` reference
--> src/main.rs:25:17
|
25 | _s.i = 1;
| ^^^^^^^^ cannot assign
error[E0597]: `s` does not live long enough
--> src/main.rs:21:26
|
21 | let mut _s = &s;
| ^^ borrowed value does not live long enough
22 | threads.push(std::thread::spawn(move || {
| __________________________-
23 | | _s.b = true;
24 | | _s.str = "b".to_string();
25 | | _s.i = 1;
26 | | }));
| |______________- argument requires that `s` is borrowed for `'static`
27 | }
28 | }
| - `s` dropped here while still borrowed
error[E0382]: borrow of moved value: `vec_s`
--> src/main.rs:34:10
|
9 | let mut vec_s = vec![];
| --------- move occurs because `vec_s` has type `Vec<S>`, which does not implement the `Copy` trait
...
19 | for s in vec_s {
| ----- `vec_s` moved due to this implicit call to `.into_iter()`
...
34 | dbg!(&vec_s);
| ^^^^^^ value borrowed here after move
|
note: this function takes ownership of the receiver `self`, which moves `vec_s`
--> /home/martin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:261:18
|
261 | fn into_iter(self) -> Self::IntoIter;
| ^^^^
help: consider iterating over a slice of the `Vec<S>`'s content to avoid moving into the `for` loop
|
19 | for s in &vec_s {
| +
You are attempting to do multithreading. Everything accessed by multiple threads has to be thread-safe. Further, with Rust being very strict (zero undefined behaviour tolerance), the compiler has to understand that your code is thread-safe.
The biggest problem here is that Rust's borrow checker doesn't understand that your threads get joined at some point. Therefore it doesn't allow you to create references to your S objects, because it cannot prove how long those references exist. Rust must be able to prove that they are destroyed before your S object is dropped; that's part of Rust's borrow checker safety guarantees.
For your specific usecase, Rust introduced thread scopes. With them, the compiler can finally understand thread lifetimes.
With some minor ownership issues fixed (use &mut vec_s for your loop, otherwise the vec_s object gets consumed by the loop), this now works:
#[derive(Debug)]
struct S {
s: String,
b: bool,
i: u128,
}
fn main() {
let mut vec_s = vec![];
vec_s.push(S {
s: "a".to_string(),
b: false,
i: 0,
});
std::thread::scope(|scope| {
let mut threads = vec![];
for s in &mut vec_s {
threads.push(scope.spawn(move || {
s.b = true;
s.s = "b".to_string();
s.i = 1;
}));
}
for thread in threads {
let _ = thread.join();
}
});
dbg!(&vec_s);
}
[src/main.rs:31] &vec_s = [
S {
s: "b",
b: true,
i: 1,
},
]
Another optimization:
If you don't actually use the JoinHandles for error propagation or similar, you don't need them at all in this example. The scope already automatically joins all threads it spawned at the end of the scope:
#[derive(Debug)]
struct S {
s: String,
b: bool,
i: u128,
}
fn main() {
let mut vec_s = vec![];
vec_s.push(S {
s: "a".to_string(),
b: false,
i: 0,
});
std::thread::scope(|scope| {
for s in &mut vec_s {
scope.spawn(move || {
s.b = true;
s.s = "b".to_string();
s.i = 1;
});
}
});
dbg!(&vec_s);
}
[src/main.rs:27] &vec_s = [
S {
s: "b",
b: true,
i: 1,
},
]

Problem regarding borrowing references in Rust

I am trying to write a program in which one thread writes to a queue and another thread
reads from the queue
But I am facing an issue regarding accessing the 'queue' in the thread that reads the queue
Below is the code which is not compiling
use ::std::collections::VecDeque;
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;
fn main() {
//let path = std::env::args()
// .nth(1)
// .expect("Argument 1 needs to be a path");
//println!("watching {}", path);
let path = "c:\\testfolder";
if let Err(e) = watch(path) {
println!("error: {:?}", e)
}
}
fn process_queue(queue: &VecDeque<String>) -> () {}
fn watch<P: AsRef<Path>>(path: P) -> notify::Result<()> {
let (tx, rx) = std::sync::mpsc::channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
let mut queue: VecDeque<String> = VecDeque::new();
thread::spawn(|| {
// everything in here runs
process_queue(&queue)
});
watcher.watch(path.as_ref(), RecursiveMode::Recursive)?;
for res in rx {
match res {
Ok(event) => {
println!("changed: {:?}", event.paths);
let os_str: String = String::from(event.paths[0].to_str().unwrap());
//let my_str: String = os_str.unwrap().to_str().unwrap();
//let s =os_str.into_os_string();
queue.push_back(os_str);
}
Err(e) => println!("watch error: {:?}", e),
}
}
Ok(())
}
The output from the Rust compiler
error[E0373]: closure may outlive the current function, but it borrows `queue`, which is owned by the current function
--> src\main.rs:43:19
|
43 | thread::spawn(|| {
| ^^ may outlive borrowed value `queue`
...
47 | process_queue(&queue)
| ----- `queue` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src\main.rs:43:5
|
43 | / thread::spawn(|| {
44 | |
45 | | // everything in here runs
46 | |
47 | | process_queue(&queue)
48 | |
49 | | });
| |______^
help: to force the closure to take ownership of `queue` (and any other referenced variables), use the `move` keyword
|
43 | thread::spawn(move || {
| ++++
error[E0502]: cannot borrow `queue` as mutable because it is also borrowed as immutable
--> src\main.rs:63:17
|
43 | thread::spawn(|| {
| - -- immutable borrow occurs here
| _____|
| |
44 | |
45 | | // everything in here runs
46 | |
47 | | process_queue(&queue)
| | ----- first borrow occurs due to use of `queue` in closure
48 | |
49 | | });
| |______- argument requires that `queue` is borrowed for `'static`
...
63 | queue.push_back(os_str);
| ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
From the errors I understand that the compiler does not allow both mutable and immutable references at the same time.
But I don't know how to implement what I am trying to do with these restrictions.
One way to solve this is by Box-ing the VecDeque so that you can pass a cloned reference to your process_queue function.
Using a Box allows you to allocate the VecDeque on the heap so that you can give your spawned thread a reference to the Vec and also still mutate the queue in the current thread.
This would look like:
let mut queue = Box::new(VecDeque::new());
let queue_clone = queue.clone();
thread::spawn(|| {
// queue_clone is now moved into the fn closure and is
// not accessible to the code below
process_queue(queue_clone)
});
and you can update process_queue to accept the correct type:
fn process_queue(queue: Box<VecDeque<String>>) -> () { }
Note that with this implementation, process_queue only runs once when the thread is spawned, and if you want to have process_queue do something every time the queue is changed, following the advice of others to use something like Channels makes the most sense.
Thanks for all your responses
From all the responses I understand that using channels and moving the receiver loop to the other thread as suggested bu user4815162342
will be the best solution
I successfully implemented what I was trying to do using channels based on your suggestions.
The final working code is pasted below
use std::thread;
use std::time::Duration;
use notify::{RecommendedWatcher, RecursiveMode, Watcher, Config};
use std::path::Path;
use std::path::PathBuf;
//
fn main() {
//let path = std::env::args()
// .nth(1)
// .expect("Argument 1 needs to be a path");
//println!("watching {}", path);
let path="c:\\testfolder";
if let Err(e) = watch(path) {
println!("error: {:?}", e)
}
}
fn watch<P: AsRef<Path>>(path: P) -> notify::Result<()> {
let (tx, rx) = std::sync::mpsc::channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
let handle=thread::spawn(move || {
// everything in here runs
for res in rx {
match res {
Ok(event) =>{
// println!("changed: {:?}", event.paths);
let os_str:String = String::from(event.paths[0].to_str().unwrap());
println!("file name: {}", os_str);
},
Err(e) => println!("watch error: {:?}", e),
}
}
});
watcher.watch(path.as_ref(), RecursiveMode::Recursive)?;
handle.join();
Ok(())
}
In your situation, using Rust's MPSC (multi-producer single-consumer, ie essentially a queue) would probably be the best. You could also create a variable that is shared between multiple thread using Arc and Mutex structs, but that would be way overkilled and can have a performance impact (only one can access the variable at any time).
Here is an example of a multi-threaded MPSC, I will let you adapt it to your infrastructure :
use std::{sync::mpsc, thread};
fn main() {
let (sender, receiver) = mpsc::channel();
let handle_1 = thread::spawn(|| {
thread_1(sender);
});
let handle_2 = thread::spawn(|| {
thread_2(receiver);
});
handle_1.join().unwrap();
handle_2.join().unwrap();
}
// the enum must have the Send trait (automatically implemented)
enum Instruction {
Print(String),
Exit
}
fn thread_1(sender: mpsc::Sender<Instruction>) {
sender.send(Instruction::Print("I".to_owned())).unwrap();
sender.send(Instruction::Print("like".to_owned())).unwrap();
sender.send(Instruction::Print("Rust".to_owned())).unwrap();
sender.send(Instruction::Print(".".to_owned())).unwrap();
sender.send(Instruction::Exit).unwrap();
}
fn thread_2(receiver: mpsc::Receiver<Instruction>) {
'global_loop: loop {
for received in receiver.recv() {
match received {
Instruction::Print(string) => print!("{} ", string),
Instruction::Exit => {
println!("");
break 'global_loop;
}
}
}
}
}

Creating threads from a list of tasks

I try to set up my first-ever thread machine and ran into the following issue.
We have this state:
use std::{thread, sync::mpsc};
fn main() {
let tasks = vec![String::from("task1"), String::from("task2")];
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
let handle = thread::spawn(move || {
for i in 0..(tasks.len()/2 - 1){
let send = format!("{}, {}", tasks[i], String::from("done"));
tx.send(send).unwrap();
}
});
for i in tasks.len()/2..tasks.len() - 1 {
let send = format!("{}, {}", tasks[i], String::from("done"));
tx1.send(send).unwrap();
}
handle.join().unwrap();
for string in rx {
println!("{}", string);
}
}
I try to set up parallel threads, which do something with the strings in the vector. They should work and send until the whole vector is covered. The vector has reliable 100 entries.
The corresponding error message:
error[E0382]: borrow of moved value: `tasks`
--> src/main.rs:19:14
|
6 | let tasks = vec![String::from("task1"), String::from("task2")];
| ----- move occurs because `tasks` has type `Vec<String>`, which does not implement the `Copy` trait
...
11 | let handle = thread::spawn(move || {
| ------- value moved into closure here
12 | for i in 0..(tasks.len()/2 - 1){
| ----- variable moved due to use in closure
...
19 | for i in tasks.len()/2..tasks.len() - 1 {
| ^^^^^ value borrowed here after move
error: aborting due to previous error
For more information about this error, try `rustc --explain E0382`.
It seems like I really do not understand how to set up parallel threads and how to treat channels together with threads and a list of tasks.
As the compiler indicates, the problem is that you moved the vector of tasks when spawning a thread, and then you try to borrow the vector from the main thread while it was already moved.
The problem you need to solve here is how to share the vectors' ownership between the main thread and the spawned thread. One way to do it is to use Arc.
Based on your code, I have wrote an example,
use std::{thread, sync::mpsc};
use std::sync::Arc;
fn main() {
let tasks = Arc::new(vec![String::from("task1"), String::from("task2")]);
let tasks_arc_clone = Arc::clone(&tasks);
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
let mid_idx = tasks.len() / 2 - 1;
let handle = thread::spawn(move || {
for i in 0..mid_idx {
let send = format!("{}, {}", tasks_arc_clone[i], String::from("done"));
tx.send(send).unwrap();
}
});
for i in mid_idx..tasks.len() {
let send = format!("{}, {}", tasks[i], String::from("done"));
tx1.send(send).unwrap();
}
drop(tx1);
for string in rx {
println!("{}", string);
}
handle.join().unwrap()
}
Rust playground link, in case you want to play with it.

Rust declare first assign later pattern

I have a bi-directional grpc stream that acts as bridge to a kafka cluster. When the stream is first initialised, I was to create the kafka consumer and start using it.
To do so, I thought of initialising an empty consumer, waiting for the first input, then assigning a created consumer to an empty one. I tried to do so by following the pattern here.
https://doc.rust-lang.org/rust-by-example/variable_bindings/declare.html
Rust is throwing a possibly-unitialized variable error, is this because it is being initialised in an asynchronous stream?
use std::pin::Pin;
use futures::{Stream, StreamExt};
use kafka::consumer::{Consumer, FetchOffset, GroupOffsetStorage};
use tonic::transport::Server;
use tonic::{Request, Response, Status};
use bridge::kafka_stream_server::{KafkaStream, KafkaStreamServer};
use bridge::{KafkaResponse, PublishRequest};
pub mod bridge {
tonic::include_proto!("bridge"); // The string specified here must match the proto package name
}
#[derive(Default)]
pub struct KafkaStreamService {}
pub fn create_kafka_consumer(topic: String) -> Consumer {
Consumer::from_hosts(vec!["localhost:9092".to_owned()])
.with_topic(topic.to_owned())
.with_fallback_offset(FetchOffset::Latest)
.with_group("".to_owned())
.with_offset_storage(GroupOffsetStorage::Kafka)
.create()
.unwrap()
}
#[tonic::async_trait]
impl KafkaStream for KafkaStreamService {
type SubscribeStream =
Pin<Box<dyn Stream<Item = Result<KafkaResponse, Status>> + Send + Sync + 'static>>;
async fn subscribe(
&self,
request: Request<tonic::Streaming<PublishRequest>>,
) -> Result<Response<Self::SubscribeStream>, Status> {
println!("Initiated stream!");
let mut stream = request.into_inner();
let mut consumer_created_flag: bool = false;
let consumer: Consumer; //declared here
let output = async_stream::try_stream! {
while let Some(publication) = stream.next().await {
let message = publication?;
let topic = message.topic.clone();
if consumer_created_flag == false {
consumer = create_kafka_consumer(topic); //error occurs here
consumer_created_flag = true;
}
let reply = bridge::KafkaResponse {
content: format!("Hello {}!", "world"),
};
yield reply.clone();
}
};
Ok(Response::new(Box::pin(output) as Self::SubscribeStream))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse().unwrap();
println!("KafkaService listening on: {}", addr);
let svc = KafkaStreamServer::new(KafkaStreamService::default());
Server::builder().add_service(svc).serve(addr).await?;
Ok(())
}
EDIT: verbose error as requested:
error[E0381]: use of possibly-uninitialized variable: `consumer`
--> src/server.rs:42:22
|
42 | let output = async_stream::try_stream! {
| ______________________^
43 | | while let Some(publication) = stream.next().await {
44 | | let message = publication?;
45 | | let topic = message.topic.clone();
46 | | if consumer_created_flag == false {
47 | | consumer = create_kafka_consumer(topic);
| | -------- use occurs due to use in generator
... |
54 | | }
55 | | };
| |_________^ use of possibly-uninitialized `consumer`
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
The declare first pattern only works with basic control flow (if, match, {}, etc). It falls apart when referenced or moved into another object, like an async block or a closure:
fn main() {
let val: i32;
let func = move || {
val = 5;
};
}
error[E0594]: cannot assign to `val`, as it is not declared as mutable
--> src/main.rs:4:9
|
2 | let val: i32;
| --- help: consider changing this to be mutable: `mut val`
3 | let func = move || {
4 | val = 5;
| ^^^^^^^ cannot assign
error[E0381]: use of possibly-uninitialized variable: `val`
--> src/main.rs:3:16
|
3 | let func = move || {
| ^^^^^^^ use of possibly-uninitialized `val`
4 | val = 5;
| --- use occurs due to use in closure
A potential fix is to move its declaration into the try_stream! macro:
let output = async_stream::try_stream! {
let mut consumer_created_flag: bool = false;
let consumer: Consumer;
while let Some(publication) = stream.next().await {
let message = publication?;
let topic = message.topic.clone();
if consumer_created_flag == false {
consumer = create_kafka_consumer(topic);
consumer_created_flag = true;
}
let reply = KafkaResponse {
content: format!("Hello {}!", "world"),
};
yield reply.clone();
}
};
However, this causes a new error because you're potentially assigning to it twice (the compiler doesn't know that consumer_created_flag is guarding it):
error[E0384]: cannot assign twice to immutable variable `consumer`
--> src\lib.rs:1348:21
|
44 | let consumer: Consumer; //declared here
| -------- help: make this binding mutable: `mut consumer`
...
49 | consumer = create_kafka_consumer(topic); //error occurs here
| ^^^^^^^^ cannot assign twice to immutable variable
Fortunately a quick fix is to simply make consumer mutable. And then the only thing the compiler complains about is that it is unused, but I figure there's a reason you've put it there.

Accessing a method of self inside a thread in Rust

I want to propagate the self struct object into a thread and then call its time_tick() method for increasing the HMS time.
pub fn start(&mut self) {
self.acti = true; // the time tick is activated now...
thread::spawn(move || {
let local_self: *mut Self = self; // this self live in the thread
loop {
thread::sleep(Duration::from_secs(1)); // wait for 1 sec
if (*local_self).acti == true { (*local_self).time_tick(); }
(*local_self).print_time(); // for debug
}
});
}
I get the error message:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/hmstimer/mod.rs:42:17
|
42 | thread::spawn(move || {
| _______________________^
43 | | let local_self: *mut Self = self; // this self live in the thread
44 | | loop {
45 | | thread::sleep(Duration::from_secs(1)); // wait for 1 sec
... |
48 | | }
49 | | });
| |_________^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 40:2...
--> src/hmstimer/mod.rs:40:2
|
40 | pub fn start(&mut self) {
| _____^
41 | | self.acti = true; // the time tick is activated now...
42 | | thread::spawn(move || {
43 | | let local_self: *mut Self = self; // this self live in the thread
... |
49 | | });
50 | | }
| |_____^
= note: ...so that the types are compatible:
expected &mut hmstimer::HMSTimer
found &mut hmstimer::HMSTimer
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure#src/hmstimer/mod.rs:42:17: 49:7 self:&mut hmstimer::HMSTimer]` will meet its required lifetime bounds
But it seems that the about method is inappropriate. What is the best practice for doing the task?
You can't pass a closure that captures a mutable reference to thread::spawn. thread::spawn needs the function to be 'static, which means that either it captures no borrows, or that all borrows are 'static. That's because the thread can continue running after the referent has been dropped.
If you don't need to use self in the original thread after calling start, then you can just pass self by value.
pub fn start(self) {
self.acti = true;
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(1));
if self.acti == true { self.time_tick(); }
self.print_time();
}
});
}
Otherwise, you'll need to use Arc to get the two threads to share ownership of Self, as well as Mutex or RwLock to synchronize reads and writes across threads.
// note: this is not a method anymore;
// invoke as `HMSTimer::start(arc.clone());`
pub fn start(this: Arc<Mutex<Self>>) {
this.lock().expect("mutex is poisoned").acti = true;
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(1));
let lock = this.lock().expect("mutex is poisoned");
if lock.acti == true { lock.time_tick(); }
lock.print_time();
// `lock` is dropped here, unlocking the mutex
}
});
}

Resources