My program is processing commands from different users. Each user has its own state that is modified by the commands he issues.
I use a HashMap to store the state of the users:
let mut states = HashMap::new();
A state is a struct:
pub struct State {
// details omitted
}
The program looks like this:
// loop
// current user is identified by 1st char of command
current_user = command[0..1].to_string()
match states.get(¤t_user) {
Some(&state) => {
println!("Switching User...");
current_state = state;
}
_ => {
println!("Creating User...");
current_state = State{...details omitted...};
states.insert(current_user, current_state);
}
}
// execute command in the State of the user
But I get the following error.
error[E0507]: cannot move out of a shared reference
42 | match states.get(¤t_user) {
| ^^^^^^^^^^^^^^^^^^^^^^^^
43 | Some(&state) => {
| ----
| |
| data moved here
| move occurs because `state` has type `Repository`, which does not implement the `Copy` trait
See Playground.
How can the program be fixed ? Do I need to use another structure than a HashMap ?
The trick is to use states.entry(..).or_insert(..) instead of states.get()...
The code is actually simpler (see playground)
current_state = states.entry(current_user)
.or_insert(State{v: String::from("P")});
Related
I had been to this question earlier because the question and the answer(s) were very useful. Yet, my case seems to be somewhat different. Hence, this post:
Because this example (and the accompanying solution/explanation) are very similar to my case, I thought of adding it here (instead of asking a new question).
I am simply trying to read the entries of a directory (Ubuntu Linux 22.10, ext4 filesystem):
let mut all_chosenfiles: Vec<&str> = Vec::new();
for next_path in path_components {
for next_dir_entry in fs::read_dir(next_path) {
for next_file in next_dir_entry {
let file_metadata = next_file.as_ref().unwrap().metadata().unwrap();
if file_metadata.is_file()
&& file_metadata.mode() & 0o500 == 0o500 {
let chosen_file = next_file.unwrap().path();
all_chosenfiles.push(chosen_file.to_owned().to_str().unwrap());
}
}
}
}
Not very idiomatic, I agree, but it serves the purpose at hand.
The compiler is not happy though:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:31:46
|
31 | all_chosenfiles.push(chosen_file.to_owned().to_str().unwrap());
| ---------------------^^^^^^^^^^^^^^^^^^^^^^-------------------- temporary value is freed at the end of this statement
| | |
| | creates a temporary which is freed while still in use
| borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
I understand what is she complaining about. I have tried to pacify her, by using let as has been prescribed here:
for next_path in path_components {
for next_dir_entry in fs::read_dir(next_path) {
for next_file in next_dir_entry {
let file_metadata = next_file.as_ref().unwrap().metadata().unwrap();
if file_metadata.is_file()
&& file_metadata.mode() & 0o500 == 0o500 {
let chosen_path = next_file.unwrap().path();
let chosen_file = &chosen_path.to_owned().to_str().unwrap();
all_chosenfiles.push(chosen_file);
}
}
}
}
She is stubborn:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:31:44
|
31 | let chosen_file = &chosen_path.to_owned().to_str().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
32 | all_chosenfiles.push(chosen_file);
| ----------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
I am scratching my head. I am missing some idiom, I am sure.
Is a derefencing using '*' operator (viz., *chosent_file) the only way out?
I have gotten around, for the time being, by:
let mut all_chosenfiles: Vec<String> = Vec::new(); // of String, and not of &str
for next_path in path_components {
for next_dir_entry in fs::read_dir(next_path) {
for next_file in next_dir_entry {
let file_metadata = next_file.as_ref().unwrap().metadata().unwrap();
if file_metadata.is_file()
&& file_metadata.mode() & 0o500 == 0o500 {
// Using as_ref() before unwrapping
let chosen_path: PathBuf = next_file.as_ref().unwrap().path();
// Doc says that, to_str()
// "Yields a &str slice if the Path is valid unicode"
let chosen_file: Option<&str> = chosen_path.to_str();
// Explicitly owning a String, made out of &str, below!
all_chosenfiles.push(chosen_file.unwrap().to_owned());
}
}
}
}
Honestly, I am not really sure why it is working. By calling next_file.as_ref(), I ensure that a reference to that memory is held. But, how does that solve the problem of temporary which is freed etc. issue? Moreover, cloning the pathstrings by using to_str() seems to be wasteful to me.
Help me fill in, the gaps in my understanding!
I am trying to write a section of code that does a web request every few seconds and updates a struct in rust. I have so far tried using a thread to accomplish this task however I am having some real trouble moving variables into the thread and assigning the results of the web request to a struct once it completes.
I have provided a small code sample below that doesn't include a web request but does demonstrate the problem I am having moving variables around.
Code:
use std::thread;
#[derive(Debug, Clone)]
struct input_holder {
i : String,
}
fn main() {
// pass this string into the thread loop.
let input = String::from("This is a string");
let input_loop = input.clone();
//assign the output of the method to this struct.
let ih = input_holder {
i : input
};
thread::spawn(|| {
let runtime = tokio::runtime::Runtime::new().unwrap();
loop {
let _ = runtime.block_on(runtime.spawn(async move {
let _new_input = match update_string(input_loop.clone(), ih.clone()){
Some(ni) => ni,
None => String::from("None")
};
}));
}
});
}
//This is where I would do the web request. I can test this method outside of the thread and it works as expected.
pub fn update_string(_input: String, mut ih: input_holder) -> Option<String> {
ih.i = String::from("This is an updated string");
Some(String::from("This is an updated string"))
}
Below is the error message I am running into:
error[E0382]: use of moved value: `ih`
--> src/main.rs:64:63
|
64 | let _ = runtime.block_on(runtime.spawn(async move {
| _______________________________________________________________^
65 | | let _new_input = match update_string(input_loop.clone(), ih.clone()){
| | -- use occurs due to use in generator
66 | | Some(ni) => ni,
67 | | None => String::from("None")
68 | | };
69 | | }));
| |_____________^ value moved here, in previous iteration of loop
|
= note: move occurs because `ih` has type `input_holder`, which does not implement the `Copy` trait
There doesn't seem to be anyway for me to pass ih that I am aware of, I don't know what use occurs due to use in generator means and Google does not seem to have much on this error (there are a bunch of options for move occurs due to use in generator but nothing for this error).
I have tried cloning and not cloning, borrowing, removing the move from the loop (I should note here I can't implement the copy trait on the struct). How are you supposed to get variables in and out of a thread loop like this?
I think I might not be understanding something about how you are supposed to move variables around in rust because in general I find myself needing to make lots and lots of copies of any value I plan to pass between methods.
The code
fn play(&self, game: &mut crate::game::Game) {
let player_in_turn: &Player = game.get_player_in_turn();
match player_in_turn.player_kind {
PlayerKind::AI => {
player_in_turn.do_random_move(&mut game);
}
_ => {
panic!("not implemented yet");
}
}
game.status = Status::ExitGame;
}
Where get_player_in_turn is
pub fn get_player_in_turn(&self) -> &Player {
match self.status {
Status::InGame(ig_status) => match ig_status {
InGameStatus::PlayFirst => {
if self.player_a.play_order == PlayOrder::First {
&self.player_a
} else {
&self.player_b
}
}
InGameStatus::PlaySecond => {
if self.player_a.play_order == PlayOrder::Second {
&self.player_a
} else {
&self.player_b
}
}
},
_ => {
panic!("get_player_in_turn called when not in a in-game status");
}
}
}
The compiler is telling me
error[E0502]: cannot borrow `game` as mutable because it is also borrowed as immutable
--> src\game\status\in_game_status.rs:28:47
|
25 | let player_in_turn: &Player = game.get_player_in_turn();
| ------------------------- immutable borrow occurs here
...
28 | player_in_turn.do_random_move(&mut game);
| -------------- ^^^^^^^^^ mutable borrow occurs here
| |
| immutable borrow later used by call
Usually I am able to understand the what and the why of compilers erros
I understand this
let player_in_turn: &Player = game.get_player_in_turn(); : I keep from game a ref to the current player; so I get an immutable ref of kind &Player
Here the game variable is immutably borrowed because of fn signature get_player_in_turn(&self). That's right, I do not want allow modifications to game in the get_player_in_turn, but also I need a ref because I need the player in turn, not a copy, or what else
player_in_turn.do_random_move(&mut game); : The instance of Player now alters the game itself doing its move
And here game should be passed as mutable because it's the goal of do_random_move
Question
I can understand the why of this but not how can I workaround
As you said, you know why this happens. Optimal workaround is to rewrite your code to use less borrowing. So you definitely can't do that if you want to modify some previously borrowed data. But if your do_random_move() function does not change internals of Players, then you can use this design pattern.
The core idea is to split borrowing into parts. Right now you are borrowing full Game structure when calling get_player_in_turn, but you only need 3 fields there: status, player_a, player_b. You can create a new function that takes them as arguments and this will split your borrowing and will allow you to borrow other fields of Game later (probably for do_random_move implementation).
here is example code
The following is my test code.
use std::rc::Rc;
enum Node {
Cons(i32, Rc<Node>),
Nil,
}
fn main() {
let list = Rc::new(Node::Cons(1, Rc::new(Node::Nil)));
match &*list {
Node::Cons(value, _next) => {
println!("value: {}", value);
}
Node::Nil => {
println!("nil");
}
}
}
My question is why do I need to use & in the match statement? If I remove the & I will get the following compilation error:
error[E0507]: cannot move out of an `Rc`
--> src/main.rs:11:11
|
11 | match *list {
| ^^^^^ help: consider borrowing here: `&*list`
12 | Node::Cons(value, _next) => {
| -----
| |
| data moved here
| move occurs because `_next` has type `Rc<Node>`, which does not implement the `Copy` trait
I don't understand the error message above. I think since list is an Rc pointer, I should dereference list to match the Node variable. On the other hand, if I remove the recursive definition then in Node (just keeping the i32) or use Box instead of Rc then I am able to match *list.
First, let's give the easy solution: as you don't need the next, you can just not try to get it:
match *list {
Node::Cons(value, _) => {
println!("value: {}", value);
}
Node::Nil => {
println!("nil");
}
}
This also hints at why your code doesn't work without &: you're requesting the node to be moved into its components, the second one not being Copy, which means the node has first to be moved out of its Rc.
But you don't normally move a value out of its Rc: a Rc is supposed to be shared so you can't remove the value without checking first there's no other smart reference to it.
If you really want to remove the node from the Rc, you can do
if let Ok(node) = Rc::<Node>::try_unwrap(list) {
match node {
Node::Cons(value, next) => {
println!("value: {}", value);
// you may use next here
}
Node::Nil => {
println!("nil");
}
}
}
But most often, if you have a Rc, what you want is either to look at it (as your code does with the &) or try get a mutable reference with get_mut which will work only if there's no other reference to it.
I have some variable passed into my function by reference. I don't need to mutate it or transfer ownership, I just look at its contents. If the contents are in some state, I want to replace the value with a default value.
For instance, my function accepts a &Vec<String> and if the Vec is empty, replace it with vec!["empty"]:
fn accept(mut vec: &Vec<String>) {
if vec.len() == 0 {
vec = &vec!["empty".to_string()];
}
// ... do something with `vec`, like looping over it
}
But this gives the error:
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:3:16
|
1 | fn accept(mut vec: &Vec<String>) {
| - let's call the lifetime of this reference `'1`
2 | if vec.len() == 0 {
3 | vec = &vec!["empty".to_string()];
| -------^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
| | |
| | creates a temporary which is freed while still in use
| assignment requires that borrow lasts for `'1````
Preventing the mut results in the same error as the previous example:
fn accept(input: &Vec<String>) {
let vec = if input.len() == 0 {
&vec!["empty".to_string()]
} else {
input
};
// ... do something with `vec`, like looping over it
}
The only solution I've come up with is to extract the default value outside the if and reference the value:
fn accept(input: &Vec<String>) {
let default = vec!["empty".to_string()];
let vec = if input.len() == 0 {
&default
} else {
input
};
// ... do something with `vec`
}
This results in less clean code and also unnecessarily doing that computation.
I know and understand the error... you're borrowing the default value inside the body of the if, but that value you're borrowing from doesn't exist outside the if. That's not my question.
Is there any cleaner way to write out this pattern?
I don't believe this is a duplicate of Is there any way to return a reference to a variable created in a function? because I have a reference I'd like to use first if possible. I don't want to dereference the reference or clone() it because that would perform unnecessary computation.
Can I store either a value or a reference in a variable at the same time?
You don't have to create the default vector if you don't use it. You just have to ensure the declaration is done outside the if block.
fn accept(input: &Vec<String>) {
let def;
let vec = if input.is_empty() {
def = vec!["empty".to_string()];
&def
} else {
input
};
// ... do something with `vec`
}
Note that you don't have to build a new default vector every time you receive an empty one. You can create it the first time this happens using lazy_static or once_cell:
#[macro_use]
extern crate lazy_static;
fn accept(input: &[String]) {
let vec = if input.is_empty() {
lazy_static! {
static ref DEFAULT: Vec<String> = vec!["empty".to_string()];
}
&DEFAULT
} else {
input
};
// use vec
}
It sounds like you may be looking for std::borrow::Cow, depending on how you're going to use it.