I'm writing a function that takes a reference to an integer and returns a vector of that integer times 2, 5 times. I think that'd look something like:
fn foo(x: &i64) -> Vec<&i64> {
let mut v = vec![];
for i in 0..5 {
let q = x * 2;
v.push(&q);
}
v
}
fn main() {
let x = 5;
let q = foo(&x);
println!("{:?}", q);
}
The borrow checker goes nuts because I define a new variable, it's allocated on the stack, and goes out of scope at the end of the function.
What do I do? Certainly I can't go through life without writing functions that create new data! I'm aware there's Box
, and Copy
-type workarounds, but I'm interested in an idiomatic Rust solution.
I realize I could return a Vec<i64>
but I think that'd run into the same issues? Mainly trying to come up with an "emblematic" problem for the general issue :)
EDIT: I only just realized that you wrote "I'm aware there's Box, Copy etc type workaround but I'm mostly interested in an idiomatic rust solution", but I've already typed the whole answer. :P And the solutions below are idiomatic Rust, this is all just how memory works! Don't go trying to return pointers to stack-allocated data in C or C++, because even if the compiler doesn't stop you, that doesn't mean anything good will come of it. ;)
Any time that you return a reference, that reference must have been a parameter to the function. In other words, if you're returning references to data, all that data must have been allocated outside of the function. You seem to understand this, I just want to make sure it's clear. :)
There are many potential ways of solving this problem depending on what your use case is.
In this particular example, because you don't need
x
for anything afterward, you can just give ownership tofoo
without bothering with references at all:But let's say that you don't want to pass ownership into
foo
. You could still return a vector of references as long as you didn't want to mutate the underlying value:...and likewise you could mutate the underlying value as long as you didn't want to hand out new pointers to it:
...but of course, you want to do both. So if you're allocating memory and you want to return it, then you need to do it somewhere other than the stack. One thing you can do is just stuff it on the heap, using
Box
:...though with the above I just want to make sure you're aware of
Box
as a general means of using the heap. Truthfully, simply using aVec
means that your data will be placed on the heap, so this works:The above is probably the most idiomatic example here, though as ever your use case might demand something different.
Alternatively, you could pull a trick from C's playbook and pre-allocate the memory outside of
foo
, and then pass in a reference to it:Finally, if the function must take a reference as its parameter and you must mutate the referenced data and you must copy the reference itself and you must return these copied references, then you can use
Cell
for this:Cell
is both efficient and non-surprising, though note thatCell
works only on types that implement theCopy
trait (which all the primitive numeric types do). If your type doesn't implementCopy
then you can still do this same thing withRefCell
, but it imposes a slight runtime overhead and opens up the possibilities for panics at runtime if you get the "borrowing" wrong.