Reputation: 2598
I have the following code:
let display_value = entry.path().display();
files_and_dirs.push(DiskEntry {
path: display_value.to_string(),
is_dir: is_dir(display_value.to_string()),
name: display_value.to_string()
});
If I write it thus:
let display_value = entry.path().display();
let dir_name = display_value.to_string();
files_and_dirs.push(DiskEntry {
path: dir_name,
is_dir: is_dir(dir_name),
name: dir_name
});
I get the following error:
move occurs because
dir_name
has typestd::string::String
, which does not implement theCopy
trait
I understand in Rust values are moved around when assigning. I want to declare one variable and use it multiple times in the second block of code. How do I do this?
Upvotes: 3
Views: 3819
Reputation: 58715
Your DiskEntry
and is_dir
are presumably defined like this:
struct DiskEntry {
path: String,
is_dir: bool,
name: String,
}
fn is_dir(path: String) -> bool {
// ...
}
Each time there is a variable of type String
, the variable needs to have its own copy of the string in memory.
As the error suggests, you could fix it by cloning:
let display_value = entry.path().display();
let dir_name = display_value.to_string();
files_and_dirs.push(DiskEntry {
path: dir_name.clone(),
is_dir: is_dir(dir_name.clone()),
name: dir_name
});
But you should avoid cloning data unnecessarily because it's inefficient. The clone that is very easy to remove is in is_dir
- it should be pretty obvious that this method doesn't need to permanently take ownership of its input. It can very easily borrow it instead:
fn is_dir(path: &str) -> bool {
// ...
}
And you would call it like this, so that the String
is borrowed as a &str
:
is_dir(&dir_name),
The other case is probably trickier. In order to avoid cloning, you will need to make the DiskEntry
struct borrow the strings. It would look like this:
struct DiskEntry<'a> {
path: &'a str,
is_dir: bool,
name: &'a str,
}
let display_value = entry.path().display();
let dir_name = display_value.to_string();
files_and_dirs.push(DiskEntry {
path: &dir_name,
is_dir: is_dir(&dir_name),
name: &dir_name
});
However, this will limit what you can do with the DiskEntry
; in particular, it cannot outlive the dir_name
variable. It's hard to know what other problems this could cause you, without knowing the wider context of your code, but it is likely to have broader impact on other data structures.
Given that you appear to be just learning Rust, if you are not yet comfortable with borrowing and lifetimes it might be easiest just to accept this one extra clone, until you have a better understanding and can make that judgement for yourself.
See also:
String
and str
?Upvotes: 8
Reputation: 1852
I want to declare one variable and use it multiple times in the second block of code. How do I do this?
You don't, because "in Rust values are moved around when assigning". The compiler doesn't automatically copy most kinds of values, because it's not clear when that's possible, and it would introduce an overhead that the programmer might want to avoid.
If you want to use the value of a Clone
variable multiple times - just clone it.
If you want to avoid repeating yourself, you can give the computation a name by using a closure.
let display_value = entry.path().display();
let dir_name = || display_value.to_string();
files_and_dirs.push(DiskEntry {
path: dir_name(),
is_dir: is_dir(dir_name()),
name: dir_name()
});
This does not let you use the value multiple times - it produces a new value with each call.
Upvotes: 0