This question already has answers here:
Spawn non-static future with Tokio
(2 answers)
Can you specify a non-static lifetime for threads? [duplicate]
(1 answer)
How do I use static lifetimes with threads?
(2 answers)
How can I send non-static data to a thread in Rust and is it needed in this example?
(1 answer)
Closed 10 months ago.
This post was edited and submitted for review 10 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I get this unhelpful error message:
error[E0597]: `msg` does not live long enough
--> src/main.rs:25:23
|
25 | let msg_str = msg.as_str();
| ^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `msg` is borrowed for `'static`
...
51 | }
| - `msg` dropped here while still borrowed
From the following Rust code:
for msg in rx {
let msg_str = msg.as_str();
let msg_type = get_msg_type(exchange, &msg_str);
println!("{}", &msg_str);
let join_handle = spawn_blocking(move || { parse_trade(exchange, market_type, &msg_str) });
match join_handle.await {
Ok(trade) => {
let t = &trade.unwrap()[0];
},
Err(error) => panic!("Problem joining: {:?}", error),
}
}
But it doesn't tell me how to do a static borrow of msg in this case.
What is a viable solution here?
None of the following proposed duplicate questions are equivalent to this question:
Spawn non-static future with Tokio
Can you specify a non-static lifetime for threads?
How do I use static lifetimes with threads?
How can I send non-static data to a thread in Rust and is it needed in this example?
The problem is that msg is a variable you get from the iteration, and therefore is only valid during one iteration step. It ceases to exist at the end of the iteration step, hence the comiler message - msg dropped here while still borrowed at the end of the loop.
The reason why this is a problem is because you move the reference msg_str into the closure via move ||, and the rust compiler cannot infer that spawn_blocking drops it before the end of the loop. (it can't infer it because it would be incorrect; join_handle.await could get cancelled, which would NOT cancel the spawned task and would cause &msg_str to be a dangling reference)
But it doesn't tell me how to do a static borrow of msg in this case.
You can't borrow statically from a temporal variable. That is the definition of a 'static borrow, that it does not cease to exist after a while.
Sadly without knowing the types of the variables / function parameters I can't give you any advice how you could solve this. You probably want to introduce a clone somewhere, but hard to tell where exactly without knowing types.
Guess of a solution
I tried to infer a minimal reproducible example from your question.
I assume that the types are as followed:
use tokio::task::spawn_blocking;
fn parse_trade(_msg: &str) -> Result<Vec<u32>, String> {
Ok(vec![1, 2, 3])
}
#[tokio::main]
async fn main() {
let rx = vec!["a".to_string(), "b".to_string()];
for msg in rx {
let msg_str = msg.as_str();
println!("{}", &msg_str);
let join_handle = spawn_blocking(move || parse_trade(&msg_str));
match join_handle.await {
Ok(trade) => {
let t = &trade.unwrap()[0];
}
Err(error) => panic!("Problem joining: {:?}", error),
}
}
}
Which does show the following error message:
error[E0597]: `msg` does not live long enough
--> src/main.rs:12:23
|
12 | let msg_str = msg.as_str();
| ^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `msg` is borrowed for `'static`
...
23 | }
| - `msg` dropped here while still borrowed
This is a possible solution:
use tokio::task::spawn_blocking;
fn parse_trade(_msg: &str) -> Result<Vec<u32>, String> {
Ok(vec![1, 2, 3])
}
#[tokio::main]
async fn main() {
let rx = vec!["a".to_string(), "b".to_string()];
for msg in rx {
let msg_str = msg.as_str();
let msg_clone = msg.clone();
println!("{}", &msg_str);
let join_handle = spawn_blocking(move || parse_trade(msg_clone.as_str()));
match join_handle.await {
Ok(trade) => {
let t = &trade.unwrap()[0];
}
Err(error) => panic!("Problem joining: {:?}", error),
}
}
}
You create a .clone() of msg and move this one into the closure instead. It carries ownership of the cloned string into the closure, and it therefore does no longer depend on the msg variable.
Related
This question already has answers here:
Double mutable borrow error in a loop happens even with NLL on
(2 answers)
Closed 11 months ago.
I'm working with a library that offers a RAII transaction API shaped like this:
struct Dataset {}
struct Transaction<'a> {
dataset: &'a mut Dataset,
}
struct Error;
impl Dataset {
fn start_transaction(&mut self) -> Result<Transaction<'_>, Error> {
Ok(Transaction { dataset: self }) // In real life, may also return Err.
}
}
Now I want to write some wrapper code that tries to start a Transaction on a dataset, but if that fails because the underlying data source does not support transactions, it returns the Dataset as-is. (Then everything is eventually resolved to a Dataset through DerefMut implementations, but that's beside the point.)
Here's my attempt:
enum TransactionIfSupported<'a> {
Transaction(Transaction<'a>),
Dataset(&'a mut Dataset),
}
fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
if let Ok(txn) = dataset.start_transaction() {
return TransactionIfSupported::Transaction(txn);
}
TransactionIfSupported::Dataset(dataset)
}
Sadly the borrow checker frowns upon it:
error[E0499]: cannot borrow `*dataset` as mutable more than once at a time
--> src/lib.rs:28:37
|
24 | fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
| -- lifetime `'a` defined here
25 | if let Ok(txn) = dataset.start_transaction() {
| --------------------------- first mutable borrow occurs here
26 | return TransactionIfSupported::Transaction(txn);
| ---------------------------------------- returning this value requires that `*dataset` is borrowed for `'a`
27 | }
28 | TransactionIfSupported::Dataset(dataset)
| ^^^^^^^ second mutable borrow occurs here
I don't quite understand why this happens. If start_transaction returns the Err variant, I'd expect any borrows from the start_transaction call to no longer be in scope after the if block. I'd expect this to hold even if there was no unconditional return statement inside the if.
What's going on and how do I resolve it?
Playground link
It looks like the borrow checker is not smart enough to handle this case yet. The linked question contains a solution for the HashMap case specifically, which will not work here because there is no contains_key equivalent.
Instead, we need some unsafe code to express it:
fn start_transaction_if_supported<'a, 'b>(dataset: &'b mut Dataset) -> TransactionIfSupported<'a, 'b> {
unsafe {
let dataset_ptr: *mut Dataset = dataset;
if let Ok(txn) = (*dataset_ptr).start_transaction() {
return TransactionIfSupported::Transaction(txn);
}
TransactionIfSupported::Dataset(&mut *dataset)
}
}
This question already has answers here:
Using a variable from a higher scope in a callback
(2 answers)
Why is Rust forcing to use move in case of i32 while spawing threads?
(1 answer)
Closed 1 year ago.
i have troubles storing a function or closure in a global static variable to be accessed by multiple functions. In my snippet i create a static variable CALLBACK, which holds the reference to a closure. The closure should have access to the local variables in the main function.
type Callback<'a> = &'a mut dyn FnMut();
static mut CALLBACK: Option<Callback> = None;
fn main() {
let s = "Hello World";
let mut listener = || {
println!("{}",s)
};
unsafe {
CALLBACK = Some(&mut listener);
}
// call timer function for testing
timer_interrupt();
}
// method would be called by timer interrupt
fn timer_interrupt() {
unsafe {
CALLBACK.as_mut().map(|f| f());
}
}
Im getting two lifetime related errors:
error[E0597]: `s` does not live long enough
--> src/main.rs:10:19
|
9 | let mut listener = || {
| -- value captured here
10 | println!("{}",s)
| ^ borrowed value does not live long enough
...
14 | CALLBACK = Some(&mut listener);
| ------------- cast requires that `s` is borrowed for `'static`
...
19 | }
| - `s` dropped here while still borrowed
error[E0597]: `listener` does not live long enough
--> src/main.rs:14:21
|
14 | CALLBACK = Some(&mut listener);
| ^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `listener` is borrowed for `'static`
...
19 | }
| - `listener` dropped here while still borrowed
Is there a way to solve it by declaring the lifetime of the closure or is there another approach how to implement the functionality i need?
the replit snipped can be found here: https://replit.com/#lutzer/StaticClosure2#src/main.rs
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
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 .
This question already has answers here:
Cannot borrow `*x` as mutable because it is also borrowed as immutable
(2 answers)
Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?
(1 answer)
Closed 5 years ago.
I want to do something similar to this code:
use std::collections::HashMap;
fn main() {
let mut vars = HashMap::<String, String>::new();
find_and_do_someth(&mut vars);
}
fn find_and_do_someth(map: &mut HashMap<String, String>) {
match map.get("test") {
None => { /* Do nothing */ }
Some(string) => do_someth(map, string),
}
}
fn do_someth(map: &mut HashMap<String, String>, val: &str) {
// Do something that alters map
}
I get the following compiler error:
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
--> src/main.rs:11:35
|
9 | match map.get("test") {
| --- immutable borrow occurs here
10 | None => { /* Do nothing */ }
11 | Some(string) => do_someth(map, string),
| ^^^ mutable borrow occurs here
12 | }
| - immutable borrow ends here
Is there a general Rust-friendly work-around for this use case?
Check something on an object
If condition is verified, alter the object
The above is just a simple example using a HashMap.
Case by case, I seem to always find a convoluted solution, however I am not used yet to the way of thinking necessary to master Rust.