Referencing a cell in multiple closures with Cursive - rust

I am using the Cursive TUI crate and I am having trouble with a dialog button because I want to set an input to change with the dialog response. This is my code:
fn prompt_repeat(siv: &mut Cursive) -> bool {
let input = Cell::new(false);
siv.add_layer(views::Dialog::text("Do you want to retype the note again?")
.title("Redo?")
.button("Yes", |s| {
input.set(true);
s.quit();
})
.button("No", |s| {
s.quit();
}));
siv.run();
input.get()
}
This causes error E0373:
error[E0373]: closure may outlive the current function, but it borrows `input`, which is owned by the current function
--> src/main.rs:94:23
|
94 | .button("No", |s| {
| ^^^ may outlive borrowed value `input`
95 | input.set(false)
| ----- `input` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:89:19
|
89 | siv.add_layer(views::Dialog::text("Do you want to retype the note again?")
| ___________________^
90 | | .title("Redo?")
91 | | .button("Yes", |s| {
92 | | input.set(true)
... |
95 | | input.set(false)
96 | | })
| |______________^
help: to force the closure to take ownership of `input` (and any other referenced variables), use the `move` keyword
|
94 | .button("No", move |s| {
| ++++
I understand that this is caused because the compiler cannot know if input will still exist before the closure uses it, but I cannot use move because I need to reference the cell again to return it (I have tried this and the compiler tells me I have borrowed the value multiple times, which is correct).
How should I go about referencing the cell in multiple closures?

I agree with #Jmb, I'd probably go with a Rc reference counting smart pointer:
fn prompt_repeat(siv: &mut Cursive) -> bool {
let input = Rc::new(Cell::new(false));
let input_2 = input.clone();
siv.add_layer(
views::Dialog::text("Do you want to retype the note again?")
.title("Redo?")
.button("Yes", move |s| {
input_2.set(true);
s.quit();
})
.button("No", move |s| {
s.quit();
}),
);
siv.run();
input.get()
}

Related

How to apply a closure which takes row and column indices over all chars in a string in Rust?

I'm trying to parse a multi-line string. Every line has the same length and the chars of the string represent numeric values on a field. I want to map these to an 2d array of u8 values. I also need to keep track of a point marked as 'P' in the input.
I went for iterating over the whole string and applying a char_parse closure to every character in the string. This closure captures a mutable reference to a point variable to extract the point while mapping all characters to numbers. To extract the point though the closure needs access to the current row and column of a given character. When enumerating lines and characters to pass row and col indices to the closure the compiler complains that a "captured variable cannot escape FnMut closure body".
My code so far:
use ndarray::{Array1, Array2};
use std::str::FromStr;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
struct Point2D {
x: usize,
y: usize,
}
#[derive(PartialEq, Debug)]
struct Field {
heights: Array2<u8>,
point: Point2D,
}
impl FromStr for Field {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut lines = s.lines().peekable();
let cols = lines.peek().ok_or("No input")?.len();
let mut point: Option<Point2D> = None;
let mut char_parse = |(row, col), chr| match chr {
c if c.is_ascii_lowercase() => Ok((c as u8) - 97),
'P' => match point {
Some(_) => Err("Duplicate point"),
None => {
point = Some(Point2D { x: col, y: row });
Ok(0)
}
},
_ => Err("Unsupported symbol"),
};
// This does not compile:
let flat_heights: Array1<u8> = lines
.enumerate()
.flat_map(|(row, line)| {
line.chars()
.enumerate()
.map(|(col, chr)| char_parse((row, col), chr))
})
.collect::<Result<_, _>>()?;
let rows = flat_heights.len() / cols;
let heights = flat_heights.into_shape((rows, cols)).map_err(|_| "Invalid shape")?;
let point = point.ok_or("No point found")?;
Ok(Field { heights, point })
}
}
#[cfg(test)]
mod tests {
use super::*;
use ndarray::array;
#[test]
fn parse_heights() {
let field: Result<Field, _> = "cab\nabP\nacc".parse();
assert_eq!(
field,
Ok(Field {
heights: array![[2, 0, 1], [0, 1, 0], [0, 2, 2],],
point: Point2D { x: 2, y: 1 },
})
);
}
}
The compiler complains about a capturing variable escaping the closure:
error: captured variable cannot escape `FnMut` closure body
--> src/question.rs:40:17
|
24 | let mut char_parse = |(row, col), chr| match chr {
| -------------- variable defined here
...
39 | .flat_map(|(row, line)| {
| - inferred to be a `FnMut` closure
40 | / line.chars()
41 | | .enumerate()
42 | | .map(|(col, chr)| char_parse((row, col), chr))
| |_______________________________________----------_________________^ returns a reference to a captured variable which escapes the closure body
| |
| variable captured here
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
error[E0373]: closure may outlive the current function, but it borrows `row`, which is owned by the current function
--> src/question.rs:42:26
|
42 | .map(|(col, chr)| char_parse((row, col), chr))
| ^^^^^^^^^^^^ --- `row` is borrowed here
| |
| may outlive borrowed value `row`
|
note: closure is returned here
--> src/question.rs:40:17
|
40 | / line.chars()
41 | | .enumerate()
42 | | .map(|(col, chr)| char_parse((row, col), chr))
| |__________________________________________________________________^
help: to force the closure to take ownership of `row` (and any other referenced variables), use the `move` keyword
|
42 | .map(move |(col, chr)| char_parse((row, col), chr))
| ++++
For more information about this error, try `rustc --explain E0373`.
But when adding that move the compiler complains that the closure must implement Copy:
error[E0507]: cannot move out of `char_parse`, a captured variable in an `FnMut` closure
--> src/question.rs:42:26
|
24 | let mut char_parse = |(row, col), chr| match chr {
| -------------- captured outer variable
...
39 | .flat_map(|(row, line)| {
| ------------- captured by this `FnMut` closure
...
42 | .map(move |(col, chr)| char_parse((row, col), chr))
| ^^^^^^^^^^^^^^^^^ ----------
| | |
| | variable moved due to use in closure
| | move occurs because `char_parse` has type `[closure#src/question.rs:24:30: 24:47]`, which does not implement the `Copy` trait
| move out of `char_parse` occurs here
For more information about this error, try `rustc --explain E0507`.
How would one get around this error? Is this the idiomatic way to do this? It would probably work to only use a single map over the whole string (and calculate row/col on the go) but is that the right approach?
Side question: Why does the char_parse closure have to be mut? AFAICT the references it captures don't change (is this bc. of interior mutability of those captured references?)
Adding move is the correct thing to do. The problem is that when you add move you are moving all captured variables, and you only want to move row and leave char_parse borrowed.
The solution is to do let char_parse = &mut char_parse; before the inner closure, now, we're moving, but we're only moving a reference to char_parse.
However, now you will get a new error:
error: captured variable cannot escape `FnMut` closure body
--> src/lib.rs:42:17
|
25 | let mut char_parse = |(row, col), chr| match chr {
| -------------- variable defined here
...
40 | .flat_map(|(row, line)| {
| - inferred to be a `FnMut` closure
41 | let char_parse = &mut char_parse;
| ---------- variable captured here
42 | / line.chars()
43 | | .enumerate()
44 | | .map(move |(col, chr)| char_parse((row, col), chr))
| |_______________________________________________________________________^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
Exactly as the error says, you cannot return a reference to a closure that captures char_reference by reference from a FnMut closure. This is because we can call the outer closure twice, yielding two closures that both concurrently have a mutable reference to char_parse, violating the borrow rules.
To fix that we need to capture char_parse by shared reference. However, trying to do that yields an error as it needs to mutate the captured point. We need to use interior mutability:
let point = std::cell::Cell::new(None);
let char_parse = |(row, col), chr| match chr {
c if c.is_ascii_lowercase() => Ok((c as u8) - 97),
'P' => match point.get() {
Some(_) => Err("Duplicate point"),
None => {
point.set(Some(Point2D { x: col, y: row }));
Ok(0)
}
},
_ => Err("Unsupported symbol"),
};
// ...
let point = point.get().ok_or("No point found")?;
Now we can do let char_parse = &char_parse; (or let char_parse = char_parse.clone();) and it will work.

Access variable after it was borrowed

I am assigning data to properties of a struct, but the following error appears:
error[E0382]: borrow of partially moved value: `super_hero`
--> src/main.rs:16:22
|
13 | for mut chunk in super_hero.description_chunks.unwrap() {
| -------- `super_hero.description_chunks` partially moved due to this method call
...
16 | println!("{:?}", &super_hero);
| ^^^^^^^ value borrowed here after partial move
|
note: this function takes ownership of the receiver `self`, which moves `super_hero.description_chunks`
--> /Users/someuser/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:752:25
|
752 | pub const fn unwrap(self) -> T {
| ^^^^
= note: partial move occurs because `super_hero.description_chunks` has type `std::option::Option<Vec<Chunk>>`, which does not implement the `Copy` trait
help: consider calling `.as_ref()` to borrow the type's contents
|
13 | for mut chunk in super_hero.description_chunks.as_ref().unwrap() {
| +++++++++
The following is the code where I am assigning data to properties of a struct:
let super_hero_description = "several years ago.";
let mut super_hero = SuperHero::new(super_hero_description);
super_hero.description_chunks = Option::from(super_hero.get_decription_chunks(DescriptionTypes::NotVillan));
for mut chunk in super_hero.description_chunks.unwrap() {
chunk.data = Option::from(chunk.get_data(DescriptionTypes::NotVillan).await);
}
println!("{:?}", &super_hero);
The error does not happen until I try to print the super_hero at the end.
Oh, and if I follow the recommendation rust gives me
for mut chunk in super_hero.description_chunks.as_ref().unwrap() {
| +++++++++
I end up with another error:
error[E0594]: cannot assign to `chunk.data`, which is behind a `&` reference
--> src/main.rs:14:9
|
13 | for mut subdivision in super_hero.description_chunks.as_ref().unwrap() {
| ------------------------------------- this iterator yields `&` references
14 | chunk.data = Option::from(chunk.get_data()(DescriptionTypes::NotVillan).await);
| ^^^^^^^^^^^^ `chunk` is a `&` reference, so the data it refers to cannot be written
rustc is correct, it just needs a little adjustment: instead of as_ref() use as_mut(), that converts &mut Option<T> to Option<&mut T> instead of &Option<T> to Option<&T>.
The problem is that unwrap() requires an owned Option: see Reference to unwrapped property fails: use of partially moved value: `self`.

cannot move out of `guess`, a captured variable in an `Fn` closure Rust

today I tried to write this MusicGuesser with GTK for fun and got this error:
error[E0507]: cannot move out of `guess`, a captured variable in an `Fn` closure
--> src/main.rs:63:32
|
15 | let mut guess = Arc::new(get_guess());
| --------- captured outer variable
16 |
17 | app.connect_activate(move |app| {
| ---------- captured by this `Fn` closure
...
63 | button.connect_clicked(move |_| {
| ^^^^^^^^ move out of `guess` occurs here
64 | let mut guess = Arc::clone(&guess);
| -----
| |
| variable moved due to use in closure
| move occurs because `guess` has type `Arc<Vec<std::string::String>>`, which does not implement the `Copy` trait
I found some same questions about this error, but I didn't understand them. There is source code: https://pastebin.com/1pNxEiB5
let guess = Arc::new(get_guess());
let cloned_guess = Arc::clone(&guess);
// ...
app.connect_activate(move |app| {
// ...
button.connect_clicked(move |_| {
let guess = cloned_guess;
// ...
If you use something in a move || closure, you move it into that closure. That means in your case you move the entire outer guess object in.
So you need to clone first, and then only move the cloned object in.
You will hit the next problem soon, though, because the content of Arc is always immutable. In Rust, you can never have multiple mutable references to the same thing. So in order to modify your guess, you will have to create interior mutability via Mutex or similar.

Borrow mutable in a loop

I try to process an array with multiple worker threads, just like Rayon's par_iter() function, but i want to have a mutable reference target, one for each thread.
I have a simple function like this
pub fn parallel_map_vector<A:Sync,T:Send,B:Send>(a:&[A], t:&mut [T], b:&mut [B], f:impl Fn(&A,&mut T)+Send+Sync){
assert_eq!(a.len(),b.len());
let atomic_counter = AtomicUsize::new(0);
crossbeam::scope(|scope| {
for target in t {
scope.spawn( |_| {
loop {
let idx = atomic_counter.fetch_add(1, Ordering::Relaxed);
if idx < a.len() {unsafe{std::slice::from_raw_parts_mut(b,b_len)};
f(&a[idx], target)
}else{
break
}
}
});
}
}).unwrap();
}
For some reason i keep getting the error
error[E0373]: closure may outlive the current function, but it borrows `target`, which is owned by the current function
--> htm/src/parallel.rs:11:26
|
9 | crossbeam::scope(|scope| {
| ----- has type `&crossbeam::thread::Scope<'1>`
10 | for target in t {
11 | scope.spawn( |_| {
| ^^^ may outlive borrowed value `target`
...
17 | f(&a[idx], target)
| ------ `target` is borrowed here
|
note: function requires argument type to outlive `'1`
--> htm/src/parallel.rs:11:13
|
11 | / scope.spawn( |_| {
12 | | loop {
13 | | let idx = atomic_counter.fetch_add(1, Ordering::Relaxed);
14 | | if idx < a.len() {
... |
21 | | }
22 | | });
| |______________^
help: to force the closure to take ownership of `target` (and any other referenced variables), use the `move` keyword
|
11 | scope.spawn( move |_| {
but when i try it in rust playground it compiles without problem
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0a10cbfd2f135975f4682c843664d539
What could be wrong here? The code looks safe to me.
The playground example to which you have linked is using the 2021 edition of Rust, which allows disjoint capture in closures (and hence the closure for each thread captures only target rather than the entire t slice). If you switch to the 2018 edition, it yields the same error as in your question.
Specify that you require the 2021 edition in your Cargo.toml (requires rustc >= 1.56.0).

Spawning threads and keeping data from them

This is a super contrived example that shows what I want to do; I have a HashMap that I want to push data in to, but I want to do it in a set of threads - the real example is that I'm connecting to a remote service inside the thread::spawn and so I want to have all of it happen in the background as much as possible so it doesn't block the primary thread.
I'm not sure how to refactor my code to allow what I'm wanting to do - any suggestions would be helpful!
let mut item_array : HashMap<i32, i32> = HashMap::new();
let mut array: [i32; 3] = [0, 1, 2];
for item in &array {
thread::spawn(|| {
item_array.insert(*item, *item);
});
}
println!("{:?}", item_array);
The errors I receive are below
error[E0597]: `array` does not live long enough
--> src/main.rs:87:17
|
87 | for item in &array {
| ^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `array` is borrowed for `'static`
...
153 | }
| - `array` dropped here while still borrowed
error[E0499]: cannot borrow `item_array` as mutable more than once at a time
--> src/main.rs:88:23
|
88 | thread::spawn(|| {
| - ^^ mutable borrow starts here in previous iteration of loop
| _________|
| |
89 | | item_array.insert(*item, *item);
| | ---------- borrows occur due to use of `item_array` in closure
90 | | });
| |__________- argument requires that `item_array` is borrowed for `'static`
error[E0373]: closure may outlive the current function, but it borrows `item`, which is owned by the current function
--> src/main.rs:88:23
|
88 | thread::spawn(|| {
| ^^ may outlive borrowed value `item`
89 | item_array.insert(*item, *item);
| ---- `item` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:88:9
|
88 | / thread::spawn(|| {
89 | | item_array.insert(*item, *item);
90 | | });
| |__________^
help: to force the closure to take ownership of `item` (and any other referenced variables), use the `move` keyword
|
88 | thread::spawn(move || {
| ^^^^^^^
error[E0373]: closure may outlive the current function, but it borrows `item_array`, which is owned by the current function
--> src/main.rs:88:23
|
88 | thread::spawn(|| {
| ^^ may outlive borrowed value `item_array`
89 | item_array.insert(*item, *item);
| ---------- `item_array` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:88:9
|
88 | / thread::spawn(|| {
89 | | item_array.insert(*item, *item);
90 | | });
| |__________^
help: to force the closure to take ownership of `item_array` (and any other referenced variables), use the `move` keyword
|
88 | thread::spawn(move || {
| ^^^^^^^
error[E0502]: cannot borrow `item_array` as immutable because it is also borrowed as mutable
--> src/main.rs:92:22
|
88 | thread::spawn(|| {
| - -- mutable borrow occurs here
| _________|
| |
89 | | item_array.insert(*item, *item);
| | ---------- first borrow occurs due to use of `item_array` in closure
90 | | });
| |__________- argument requires that `item_array` is borrowed for `'static`
91 | }
92 | println!("{:?}", item_array);
| ^^^^^^^^^^ immutable borrow occurs here
There are two problems here (which are typical problems with multithreaded Rust):
Your thread cannot borrow any data that may outlive it (which, when using std::thread::spawn, is any data1).
Multiple thread cannot borrow the same mutable data.
The first problem is typically solved by:
Copying data instead of referencing it. This is particularly useful for primitive types, such as integers.
Using scoped threads.
Using Arc for thread-safe shared pointer, to ensure that data outlives all threads using it.
Not all are possible in all cases, but I'd recommend the above solutions in that order.
The second problem is typically solved with locks such as Mutex or RwLock, which allows only a single thread to get a mutable reference at a time.
Combining these, I'd solve your example like this:
// mutable data is wrapped in a Mutex
let item_array: Mutex<HashMap<i32, i32>> = Mutex::new(HashMap::new());
// immutable data does not have to be wrapped with scoped threads
let array: [i32; 3] = [0, 1, 2];
// using crossbeam::scope from the crossbeam library, which lets
// us reference variables outside the scope (item_array and array)
crossbeam::scope(|s| {
for item in &array {
// copy item (since it is an integer)
let item = *item;
// explicitly reference item_array, since we'll later need to move this
let item_array_ref = &item_array;
// move item and item_array_ref into the closure (instead of referencing,
// which is by default)
s.spawn(move |_| {
// need to call .lock() to aquire a mutable reference to the HashMap
// will wait until the mutable reference is not used by any other threads
let mut item_array_lock = item_array_ref.lock().unwrap();
item_array_lock.insert(item, item);
});
}
// the scope will persist until all threads spawned inside it (with s.spawn) have completed, blocking the current thread,
// ensuring that any referenced data outlives all the threads
})
.unwrap();
// need to call .lock() here as well to get a reference to the HashMap
println!("{:?}", item_array.lock().unwrap());
Run in playground
1Except when the data has 'static lifetime, i.e. can exist for the entire lifetime of the program.
Here is very simple example of code based on source you have provieded:
use std::collections::HashMap;
use std::thread;
fn main() {
let item_array: HashMap<i32, i32> = HashMap::new();
let array: [i32; 3] = [0, 1, 2];
let mut handles = vec![];
let mutex = std::sync::Mutex::new(item_array);
let arc = std::sync::Arc::new(mutex);
for item in &array {
let item = item.to_owned();
let arc_cloned = std::sync::Arc::clone(&arc);
let th = thread::spawn(move || {
let mut guard = arc_cloned.lock().unwrap();
guard.insert(item, item);
});
handles.push(th);
}
for handle in handles {
handle.join().unwrap();
}
println!("{:?}", arc.lock().unwrap());
}
And you can play it here on rust playground

Resources