2
votes

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 type std::string::String, which does not implement the Copy 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?

2
Either pass them by borrow e.g. is_dir(&dir_name) if your function allows this or use clone method of String.Leśny Rumcajs
passing them in to is_dir by borrowing is not the problem. I want to declare one variable and use it in three places like in the second block of code. Whats the right way to do this in rust?shashanka n
These three places must not take ownership of the data you're passing so if path field is declared as String you can pass your single variable there but you have to clone it / use to_owned. There and everywhere it would take ownership.Leśny Rumcajs
@shashankan If you want to use it in 3 places, then you need to either (a) clone the value twice, or (b) borrow the value twice.Peter Hall

2 Answers

6
votes

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:

-1
votes

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.