If expressions that yield either value or reference?

和自甴很熟 提交于 2021-02-09 11:12:16

问题


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
}

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`

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
}

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`
}

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)


回答1:


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
}



回答2:


It sounds like you may be looking for std::borrow::Cow, depending on how you're going to use it: https://doc.rust-lang.org/std/borrow/enum.Cow.html



来源:https://stackoverflow.com/questions/58559969/if-expressions-that-yield-either-value-or-reference

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!