shashanka n
shashanka n

Reputation: 2598

Reusing variables in Rust

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?

Upvotes: 3

Views: 3819

Answers (2)

Peter Hall
Peter Hall

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:

Upvotes: 8

Isaac van Bakel
Isaac van Bakel

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

Related Questions