Reputation: 741
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
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
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