Value used after move? - rust

My code:
fn add(&mut self, key: &str, path: std::path::PathBuf) {
self.key.push(key.to_string());
self.path.push(path);
if key == "img" {
self.img.push(self.create_img(path))
}
}
Error:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/software/init/image.rs:44:27
|
44 | self.img.push(self.create_img(path))
| -------- ---- ^^^^ second mutable borrow occurs here
| | |
| | first borrow later used by call
| first mutable borrow occurs here
error[E0382]: use of moved value: `path`
--> src/software/init/image.rs:44:43
|
40 | fn add(&mut self, key: &str, path: std::path::PathBuf) {
| ---- move occurs because `path` has type `std::path::PathBuf`, which does not implement the `Copy` trait
41 | self.key.push(key.to_string());
42 | self.path.push(path);
| ---- value moved here
43 | if key == "img" {
44 | self.img.push(self.create_img(path))
| ^^^^ value used here after move

It is fairly hard to reason about this code without other methods, but the following code should work (or at least be closer to working solution):
fn add(&mut self, key: &str, path: std::path::PathBuf) {
self.key.push(key.to_string());
// Here, path must be explicitly cloned because of move semantics.
self.path.push(path.clone());
if key == "img" {
// Sometimes compiler cannot determine dependencies of compound
// statement, so with `img` bounding `img` we un-borrow `self`
// (if `create_img` returns owned type). Otherwise you need some
// reference counter like std::rc::Rc.
let img = self.create_img(path);
self.img.push(img);
}
}

Related

How do I access shared memory mutable and immutable in separate threads

I have a multithreaded program that wants to access resources across threads. Some want to write to them, some want to read from them.
I'm not sure if this counts as a global mutable singleton, because my setup isn't global, but the solution might be similar?
A very much simplified version would be the code below.
What it tries to do, is have one thread write to a struct, and another read from this same struct. So when thread A mutates the data, thread B would read the mutated data then.
use std::{thread, time::Duration};
struct TagList {
list: Vec<String>
}
impl TagList {
fn add(self: &mut TagList, tag: String) {
self.list.push(tag);
}
fn read(&self) -> Vec<String> {
self.list.clone()
}
}
fn main() {
let mut list = TagList { list: vec![] };
thread::spawn(move || {
["fee", "foo", "faa", "fuu"].into_iter().for_each(|tag| {
list.add(tag.to_string());
thread::sleep(Duration::from_millis(100));
});
});
thread::spawn(move || {
loop {
dbg!(list.read());
thread::sleep(Duration::from_millis(20));
}
});
}
This, rather obviously, fails with a borrow error:
error[E0382]: use of moved value: `list`
--> src/main.rs:79:19
|
70 | let mut list = TagList { list: vec![] };
| -------- move occurs because `list` has type `TagList`, which does not implement the `Copy` trait
71 |
72 | thread::spawn(move || {
| ------- value moved into closure here
73 | ["fee", "foo", "faa", "fuu"].into_iter().for_each(|tag| {
74 | list.add(tag.to_string());
| ---- variable moved due to use in closure
...
79 | thread::spawn(move || {
| ^^^^^^^ value used here after move
80 | dbg!(list.read());
| ---- use occurs due to use in closure
I've tried to solve this by wrapping the list in an Arc:
use std::sync::Arc;
// ...
let list = Arc::new(TagList { list: vec![] });
let write_list = Arc::get_mut(&mut list).unwrap();
let read_list = Arc::clone(&list);
thread::spawn(move || {
["fee", "foo", "faa", "fuu"].into_iter().for_each(|tag| {
write_list.add(tag.to_string());
thread::sleep(Duration::from_millis(100));
});
});
thread::spawn(move || {
loop {
dbg!(read_list.read());
thread::sleep(Duration::from_millis(20));
}
});
This fails because I probably don't understand how Arc is supposed to work or how it relates to lifetimes:
error[E0597]: `list` does not live long enough
--> src/main.rs:71:35
|
71 | let write_list = Arc::get_mut(&mut list).unwrap();
| -------------^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `list` is borrowed for `'static`
...
85 | }
| - `list` dropped here while still borrowed
error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable
--> src/main.rs:72:32
|
71 | let write_list = Arc::get_mut(&mut list).unwrap();
| -----------------------
| | |
| | mutable borrow occurs here
| argument requires that `list` is borrowed for `'static`
72 | let read_list = Arc::clone(&list);
| ^^^^^ immutable borrow occurs here
Is what I want possible at all? (I'm pretty sure i've seen this used with e.g. std::sync::mpsc where somehow messages are pushed and read over threads).
What should I use? Is Arc the proper structure for this, or am I looking at the wrong solution there? What should I read up on to understand how such problems are typically solved in Rust?
Arc does not allow mutation, and Arc::get_mut() is not a solution to that. It allows mutation when the Arc has only one instance (thus the second error) and return a reference which is not 'static, therefore you cannot move it into a thread (the first error).
If you need to mutate the content of an Arc, use Mutex or RwLock.

How can I use a channel between an async closure and my main thread in rust?

I am trying to use a channel to communicate between an event handler and the main thread of my program using async rust. The event handler in question is this one from the matrix-rust-sdk.
I can see that this exact pattern is used in the code here.
But when I tried literally the same thing in my own code, it gives me a really strange lifetime error.
error: lifetime may not live long enough
--> src/main.rs:84:13
|
80 | move |event: OriginalSyncRoomMessageEvent, room: Room| {
| ------------------------------------------------------
| | |
| | return type of closure `impl futures_util::Future<Output = ()>` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
...
84 | / async move {
85 | | if let Room::Joined(room) = room {
86 | | if room.room_id() == room_id {
87 | | match event.content.msgtype {
... |
94 | | }
95 | | }
| |_____________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
I tried to make a much simpler example, and the weird lifetime error remains:
use tokio::sync::mpsc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (tx, _rx) = mpsc::channel(32);
let closure = move || async {
at.send("hi");
};
Ok(())
}
Gives me:
error: lifetime may not live long enough
--> src/main.rs:9:27
|
9 | let closure = move || async {
| ___________________-------_^
| | | |
| | | return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | at.send("hi");
11 | | };
| |_____^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
So how can I use a channel in an async closure? Why doesn't my code work when the code in the matrix-rust-sdk does?
I think you meant || async move instead of move || async.
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, _rx) = mpsc::channel(32);
let closure = || async move {
tx.send("hi").await.unwrap();
};
closure().await;
}
I think in most cases, |args| async move {} is what you want to use if you want to create an async closure. But I don't completely understand the differences either.
For more infos, this might help:
What is the difference between `|_| async move {}` and `async move |_| {}`.
I don't think your minimal example represents your actual problem of your real code, though. This is a minimal example that represents the real problem:
#[derive(Clone, Debug)]
struct RoomId(u32);
#[derive(Clone, Debug)]
struct Room {
id: RoomId,
}
impl Room {
fn room_id(&self) -> &RoomId {
&self.id
}
}
#[tokio::main]
async fn main() {
let dm_room = Room { id: RoomId(42) };
let dm_room_closure = dm_room.clone();
let closure = move || {
let room_id = dm_room_closure.room_id();
async move {
println!("{}", room_id.0);
}
};
closure().await;
}
error: lifetime may not live long enough
--> src/main.rs:23:9
|
20 | let closure = move || {
| -------
| | |
| | return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
...
23 | / async move {
24 | | println!("{}", room_id.0);
25 | | }
| |_________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
The real problem here is caused by the fact that room_id contains a reference to dm_room_closure, but dm_room_closure does not get kept alive by the innermost async move context.
To fix this, make sure that the async move keeps dm_room_closure alive by moving it in as well. In this case, this is as simple as creating the room_id variable inside of the async move:
#[derive(Clone, Debug)]
struct RoomId(u32);
#[derive(Clone, Debug)]
struct Room {
id: RoomId,
}
impl Room {
fn room_id(&self) -> &RoomId {
&self.id
}
}
#[tokio::main]
async fn main() {
let dm_room = Room { id: RoomId(42) };
let dm_room_closure = dm_room.clone();
let closure = move || {
async move {
let room_id = dm_room_closure.room_id();
println!("{}", room_id.0);
}
};
closure().await;
}
42
So I finally fixed the error. It turns out that something in Room doesn't implement Copy and therefore it was causing some sort of state sharing despite the Clones. I fixed it by passing the RoomId as a string. Since the lifetime error message is entirely opaque, there was no way to see which moved variable was actually causing the problem. Off to file a compiler bug report.

mutate rust value between threads with crossbeam

I am modifiying a crate (rust-argon2) to some custom needs. That crate uses multithreading with scoped threads using crossbeam. I want to have a mutable state between threads, that may depend on each other.
The following snippet showcases the situation: fill_memory_blocks_mt gets some mutable state reference state, that will be mutated in fill_segment across multiple threads using crossbeam_utils::thread::scope. They are synced via common::SYNC_POINTS.
fn fill_memory_blocks_mt(context: &Context, memory: &mut Memory, state: &mut Argon2Result) {
for p in 0..context.config.time_cost {
for s in 0..common::SYNC_POINTS {
let _ = scope(|scoped| {
for (l, mem) in (0..context.config.lanes).zip(memory.as_lanes_mut()) {
let position = Position {
pass: p,
lane: l,
slice: s,
index: 0,
};
scoped.spawn(move |_| {
fill_segment(context, &position, mem, state);
});
}
});
}
}
}
The compiler outputs following message:
error[E0382]: use of moved value: `state`
--> src/core.rs:223:34
|
223 | scoped.spawn(move |_| {
| ^^^^^^^^ value moved into closure here, in previous iteration of loop
224 | fill_segment(context, &position, mem, state);
| ----- use occurs due to use in closure
|
= note: move occurs because `state` has type `&mut Argon2Result`, which does not implement the `Copy` trait
help: consider creating a fresh reborrow of `state` here
|
223 | scoped.spawn(&mut *move |_| {
| ++++++
error[E0382]: use of moved value: `state`
--> src/core.rs:215:27
|
212 | fn fill_memory_blocks_mt(context: &Context, memory: &mut Memory, state: &mut Argon2Result) {
| ----- move occurs because `state` has type `&mut Argon2Result`, which does not implement the `Copy` trait
...
215 | let _ = scope(|scoped| {
| ^^^^^^^^ value moved into closure here, in previous iteration of loop
...
224 | fill_segment(context, &position, mem, state);
| ----- use occurs due to use in closure
|
help: consider creating a fresh reborrow of `state` here
|
215 | let _ = scope(&mut *|scoped| {
| ++++++
For more information about this error, try `rustc --explain E0382`.
error: could not compile `rust-argon2-wasm` due to 2 previous errors
Of course I don't want to copy state but use one instance for all threads. They don't overwrite fields between each other, but may depend on each other.

Lifetime issues when refactoring into function with closure back-call

I am writing a Rust application that uses the wgpu library to render stuff. How the library works is largely unimportant, since the errors I'm facing are lifetime-related.
In the function that actually performs the rendering looks like this: (You don't need to understand it, I listed it largely for completeness' sake.)
pub fn render(&self) -> anyhow::Result<()> {
let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { label: Some("render encoder") }
);
let render_pass_descriptor = wgpu::RenderPassDescriptor {
label: Some("render pass"),
color_attachments: &[
wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(
wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 }),
store: false,
}
},
],
depth_stencil_attachment: None,
};
let mut render_pass = encoder.begin_render_pass(&render_pass_descriptor);
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..self.num_vertecies, 0..1);
// Explicitly drop, bc. it borrows the encoder.
drop(render_pass);
self.queue.submit(iter::once(encoder.finish()));
output.present();
Ok(())
}
I wanted to refactor this piece of code into a utility function, but keep the three calls on the render_pass object.
The utility function has this signature and does the same stuff the original function did, but instead of the three calls on render_pipeline, it just calls the render_pass_configurator closure:
pub fn submit_render_pass<F: FnOnce(wgpu::RenderPass)>(
surface: &wgpu::Surface,
device: &wgpu::Device,
queue: &wgpu::Queue,
clear_color: wgpu::Color,
render_pass_configurator: F,
) -> anyhow::Result<()> { ... }
And the body of the original render() function is replaced with the call to this utility function:
util::submit_render_pass(&self.surface, &self.device, &self.queue,
wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 },
| mut render_pass | {
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..self.num_vertecies, 0..1);
},
)
Seems straightforward to me, but of course Rust's borrow checker disagrees:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/gpu.rs:89:38
|
89 | render_pass.set_pipeline(&self.render_pipeline);
| ^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> src/gpu.rs:85:19
|
85 | pub fn render(&self) -> anyhow::Result<()> {
| ^^^^^
note: ...so that reference does not outlive borrowed content
--> src/gpu.rs:89:38
|
89 | render_pass.set_pipeline(&self.render_pipeline);
| ^^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #1 defined here...
--> src/gpu.rs:88:9
|
88 | / | mut render_pass: wgpu::RenderPass | {
89 | | render_pass.set_pipeline(&self.render_pipeline);
90 | | render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
91 | | render_pass.draw(0..self.num_vertecies, 0..1);
92 | | },
| |_________^
note: ...so that the types are compatible
--> src/gpu.rs:89:25
|
89 | render_pass.set_pipeline(&self.render_pipeline);
| ^^^^^^^^^^^^
= note: expected `&mut RenderPass<'_>`
found `&mut RenderPass<'_>`
(... and a similar error for the .slice(..) call.)
I understand that because of the render_pass.set_pipeline(&self.render_pipeline) call, render_pass may not ever live longer than &self. But it doesn't. render_pipeline gets dropped at the end of the closure, and &self lives on.
I tried adding lifetimes to the best of my ability, and I got the error to change only once when I added an explicit lifetime to the type of the utility function, changing the closure definition to F: FnOnce(wgpu::RenderPass<'a>): (But this error didn't make much sense to me either.)
error[E0597]: `view` does not live long enough
--> src/gpu_util.rs:130:23
|
112 | pub fn submit_render_pass<'a, F: FnOnce(wgpu::RenderPass<'a>)>(
| -- lifetime `'a` defined here
...
130 | view: &view,
| ^^^^^ borrowed value does not live long enough
...
143 | render_pass_configurator(render_pass);
| ------------------------------------- argument requires that `view` is borrowed for `'a`
...
149 | }
| - `view` dropped here while still borrowed
Update
I got it to work by writing the render() function like this: (Self == GpuState)
pub fn render(&self) -> anyhow::Result<()> {
fn configure_render_pass<'a>(s: &'a GpuState, mut render_pass: wgpu::RenderPass<'a>) {
render_pass.set_pipeline(&s.render_pipeline);
render_pass.set_vertex_buffer(0, s.vertex_buffer.slice(..));
render_pass.draw(0..s.num_vertecies, 0..1);
}
util::submit_render_pass(&self.surface, &self.device, &self.queue,
wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 },
| render_pass: wgpu::RenderPass<'_> | {
configure_render_pass(self, render_pass);
},
)
}
I think what makes it work here is that I get a chance to explicitly tell the compiler that the captured self lives as long as the render_pass. But that's only my guess...
I'll leave the question open, if anyone comes up with a solution to make it work without the extra function declaration.
pub fn submit_render_pass<F: FnOnce(wgpu::RenderPass)>(
surface: &wgpu::Surface,
device: &wgpu::Device,
queue: &wgpu::Queue,
clear_color: wgpu::Color,
render_pass_configurator: F,
) -> anyhow::Result<()> { ... }
First of, all, you have a hidden lifetime parameter, which can give rise to very confusing errors. Add the #![deny(elided_lifetimes_in_paths)] lint setting to your code to force these to be explicit. (It's unfortunate that that lint isn't on by default.) You'll be required to change the code to…
pub fn submit_render_pass<F: FnOnce(wgpu::RenderPass<'???>)>(
And now we see part of the problem: what goes in the spot I've marked '???? The RenderPass borrows from the CommandEncoder (the signature of CommandEncoder::begin_render_pass() tells us that), but the CommandEncoder is a local variable inside submit_render_pass(), so borrows of it cannot have a lifetime that is nameable from outside the function.
To solve this problem, you need to use a HRTB to specify that the callback function must be able to accept any lifetime:
pub fn submit_render_pass<F>(
surface: &wgpu::Surface,
device: &wgpu::Device,
queue: &wgpu::Queue,
clear_color: wgpu::Color,
render_pass_configurator: F,
) -> anyhow::Result<()>
where
F: for<'encoder> FnOnce(wgpu::RenderPass<'encoder>)
{ ... }

Implement tree data structure

I want to implement a tree data structure. I have a Node struct and want it to hold references to child Nodes. I tried:
use std::collections::*;
#[derive(Debug)]
struct Node {
value: String,
children: HashMap<String, Node>,
}
impl Node {
fn new(value: String) -> Self {
Node {
value: value,
children: HashMap::new(),
}
}
fn add_child(&mut self, key: String, value: String) -> &mut Node {
let mut node = Node::new(value);
self.children.insert(key, node);
&mut node
}
}
fn main() {
let mut root_node = Node::new("root".to_string());
root_node.add_child("child_1_1".to_string(), "child_1_1_value".to_string());
}
This code does not compile:
error: `node` does not live long enough
--> src/main.rs:22:10
|
22 | &mut node
| ^^^^ does not live long enough
23 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 19:67...
--> src/main.rs:19:68
|
19 | fn add_child(&mut self, key: String, value: String) -> &mut Node {
| ____________________________________________________________________^ starting here...
20 | | let mut node = Node::new(value);
21 | | self.children.insert(key, node);
22 | | &mut node
23 | | }
| |___^ ...ending here
error[E0382]: use of moved value: `node`
--> src/main.rs:22:10
|
21 | self.children.insert(key, node);
| ---- value moved here
22 | &mut node
| ^^^^ value used here after move
|
= note: move occurs because `node` has type `Node`, which does not implement the `Copy` trait
How can I implement this?
In this case, it's actually necessary to look at the second error message in the compiler output:
error[E0382]: use of moved value: `node`
--> src/main.rs:22:10
|
21 | self.children.insert(key, node);
| ---- value moved here
22 | &mut node
| ^^^^ value used here after move
|
= note: move occurs because `node` has type `Node`, which does not implement the `Copy` trait
The variable node is moved into the hashmap in line 21. You can't use it afterwards! In Rust we have move semantics, meaning that everything gets moved by default instead of cloned by default (C++) or referenced by default (Java). You want to return a reference to the Node object inside the hashmap!
An easy way would be to insert node as you are already doing and afterwards fetching the value from the hashmap:
let mut node = Node::new(value);
self.children.insert(key.clone(), node);
self.children.get_mut(key).unwrap()
This should make clear what the function actually does. However, this code has some disadvantages: First, we have to clone key (we need it for the insertion and the query) and secondly, the hashmap needs calculate the hash of the key twice which is not very efficient.
Luckily, Rust's HashMap has a nice entry()-API. We could change the function like that:
self.children.entry(key).or_insert_with(|| Node::new(value))
This is the whole body of add_child()! Now, however, we notice that ... we haven't really thought about what is supposed to happen if the hashmap already contains a value associated with the given key! In the code above, the old value is kept and returned. If you want to do something else (e.g. replace the value), you could just use match on the Entry object:
let node = Node::new(value);
match self.children.entry(key) {
Entry::Occupied(e) => {
// Maybe you want to panic!() here... but we will just
// replace the value:
e.insert(node); // discarding old value...
e.get_mut()
}
Entry::Vacant(e) => insert(node),
}

Resources