Reputation: 78
I realized that very often in Rust I need to do a following pattern:
let variable = &some_ref;
let variable = if something {
let new_variable = create_something();
&new_variable
} else {
variable
};
// Use variable here
In other words, I need to either use an existing reference or create a new owned value and use a reference to it.
But the problem is if I do it like in the example above the new_variable
does not live long enough, it's dropped at the end of the first if
clause.
Is there a idiomatic way to structure the code nicely to achieve the "use reference or create new and use reference to new" way? Or I just have to copy/make function for the code that uses variable
2 times - one for branch where I already have reference and another for branch where I create a owned value and that use reference to it?
Here is real-world example of how I usually use function (overlay_with_u32
in this case) to copy the behavior between 2 branches:
let source = &source;
if is_position_negative(x, y) {
let source = crop(source, x, y);
overlay_with_u32(destination, &source, x, y);
} else {
overlay_with_u32(destination, source, x, y);
};
Upvotes: 2
Views: 113
Reputation: 71545
Using Cow
may be the right thing to do, but here I'm going to suggest another approach, that might be cheaper (especially if the types are Drop
-less, in this case it is zero-cost except it may require more stack space), but requires more code and may be less obvious.
Rust allows you to declare a variable but initialize it conditionally, as long as the compiler can prove that the variable is always initialized if it is used. You can exploit this fact to longer the lifetime of a variable inside a scope; instead of:
let reference = {
let variable = ...;
&variable
};
You can write:
let variable;
let reference = {
variable = ...;
&variable
};
But now variable
lives long enough.
Applied to your case, it looks like:
let variable = &some_ref;
let new_variable;
let variable = if something {
new_variable = create_something();
&new_variable
} else {
variable
};
Upvotes: 2
Reputation: 58865
You can use Cow
(Clone-on-write) for this.
The Cow
type is an emum of either an owned or a borrowed value:
pub enum Cow<'a, B>
where
B: 'a + ToOwned + ?Sized,
{
Borrowed(&'a B),
Owned(<B as ToOwned>::Owned),
}
You could use it something like (variables renamed for clarity):
use std::borrow::Cow;
let variable_ref = &some_ref;
let variable = if something {
let variable_created = create_something();
Cow::Owned(variable_created)
} else {
Cow::Borrowed(variable_ref)
};
Functions that accept a &T
can be given a &Cow<T>
, which will automatically be deferenced as you'd expect:
let variable: Cow<'_, i32> = Cow::Owned(3);
do_stuff(&variable);
fn do_stuff(i: &i32) {
println!("{}", i);
}
Upvotes: 5