If expressions that yield either value or reference?

Chr*_*ith 3 rust

I have this pattern that shows up every now and then, but I haven't found a nice way to implement it "correct".

What it is, is I have some variable passed into my function by reference. I don't need to mutate it, I don't need to transfer ownership, I just look at its contents.

However, if the contents are in some state, replace the value with a default value.

For instance say my function accepts a &Vec<String> and if the vec is empty, replace it with vec!["empty"]. One might implement that like so:

fn accept(mut vec: &Vec<String>) {
    if vec.len() == 0 {
        vec = &vec!["empty".to_string()];
    }
    // ... do something with `vec`, like looping over it
}
Run Code Online (Sandbox Code Playgroud)

But this gives the error:

| fn accept(mut vec: &Vec<String>) {
|                    - let's call the lifetime of this reference `'1`
|     if vec.len() == 0 {
|         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`
Run Code Online (Sandbox Code Playgroud)

A slightly more "well-designed" function might look like this (preventing the mut):

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
}
Run Code Online (Sandbox Code Playgroud)

However this still results in the same error as the previous example.

The only solution I've come up with is to extract the default value outside the if and just 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`
}
Run Code Online (Sandbox Code Playgroud)

However, that 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.

My question is: is there any cleaner way to write out this pattern?

I don't believe this is a duplicate of this because unlike them, I have a reference I'd like to use first if possible. I don't want to dereference reference or clone() it because that would perform unnecessary computation. And that's where the title of my question comes from: can I store either a value or a reference in a variable at the same time? (of course, probably not, but that's what I want to do)

Den*_*ret 5

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`
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不必在每次收到一个空矢量时都建立一个新的默认矢量。您可以在第一次发生这种情况时使用lazy_static或创建它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
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

90 次

最近记录:

5 年,10 月 前