TSK
TSK

Reputation: 741

Create owned value on demand

There is an existing value a, and I want to get a reference to either a or a new value b created on demand, depending on some condition. The code below won't compile. I would like to know what is the idiomatic way of doing so in Rust.

fn main() {
    let condition = false;

    let a: String = "a".to_string();
    let r: &String = if condition {
        &a
    } else {
        let b: String = "b".to_string();
        &b
    };
}

New example (in response to @PitaJ):

struct S(i32);

fn main() {
    let condition = false;

    let a: S = S(0);
    let r: &S = if condition {
        &a
    } else {
        let b: S = S(1);
        &b
    };
}

Upvotes: 1

Views: 102

Answers (2)

fred xia
fred xia

Reputation: 149

The code is somewhat equivalent to the C++ code below:

#include <string>

int main()
{
    auto condition = false;
    std::string a("a");
    const std::string* ptr;
    if (condition) {
        ptr = &a;
    } else {
        std::string b("b");
        ptr = &b;
    }
}

If you are familiar with C++ you will see that ptr has the address of b, which is freed after code execution exits else {...} block. So ptr is pointing to an invalid memory address.

C++ compiler won't complain. This code will pass the C++ compiler. However code like this will have runtime problems. It's a good thing that Rust compiler catches such error at compile time, and force you to make sure that the lifetime of a copy of b is stored in r.

Upvotes: -1

PitaJ
PitaJ

Reputation: 15012

Here's how to use Cow in your case:

use std::borrow::Cow;

let condition = false;

let a: String = "a".to_string();
let r: Cow<str> = if condition {
    Cow::from(&a)
} else {
    let b: String = "b".to_string();
    Cow::from(b)
};

For your second example, you have to add Clone. For an arbitrary clone type, you also have to instantiate the variants directly.

use std::borrow::Cow;

#[derive(Clone)]
struct S(i32);

let condition = false;

let a: S = S(0);
let r: Cow<S> = if condition {
    Cow::Borrowed(&a)
} else {
    let b: S = S(1);
    Cow::Owned(b)
};

And this does mean that you can use Cow<String> in the same way.

use std::borrow::Cow;

let condition = false;

let a: String = "a".to_string();
let r: Cow<String> = if condition {
    Cow::Borrowed(&a)
} else {
    let b: String = "b".to_string();
    Cow::Owned(b)
};

It's better to use Cow<str> for the same reason that it's better to use &str instead of &String: &str has a smaller representation in memory (one fewer pointer-sized field), and has all of the same capabilities.

Upvotes: 2

Related Questions