With the following simplified/abstracted version of my problem:
fn main() {
let foo_selectors = vec![0, 1];
let foos: Vec<_> = foo_selectors
.into_iter()
.flat_map(|i| get_foo(i).into_iter())
.collect();
println!("{:?}", foos);
}
fn get_foo(i: u8) -> [u8; 3] {
if i % 2 == 0 {
[1, 2, 3]
} else {
[4, 5, 6]
}
}
Playground Link
I get the following error message:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:6:44
|
6 | .flat_map(|i| get_foo(i).into_iter())
| ---------- ^ temporary value dropped here while still borrowed
| |
| temporary value created here
...
10 | }
| - temporary value needs to live until here
The error message complains about borrowing, but I've used into_iter
everywhere, which as I understand it, takes ownership of the iterated values. I want to take ownership of the values returned from get_foo
and insert their elements into foos
. How can I do that?
How to take ownership of a value in flat_map
There's nothing special about flat_map
here. When calling get_foo
, ownership of the return value is transferred to the caller, just like anywhere else in Rust.
I've used into_iter everywhere, which as I understand it, takes ownership of the iterated values.
Usually, yes, but not for an array. See the linked questions below for why. This is the source of the problem. The flat_map
closure owns the result of get_foo
, and then you take a reference to it. The reference cannot live beyond the closure, but that's what you are asking it to do.
As a workaround, you can return a Vec
from your function:
fn get_foo(i: u8) -> Vec<u8> {
if i % 2 == 0 {
vec![1, 2, 3]
} else {
vec![4, 5, 6]
}
}
You could also convert the returned array into a Vec
inside the flat_map
call.
If you felt like the situation warranted it, you could implement your own iterator:
struct CloneArrayIter<T> {
arr: [T; 3],
idx: usize,
}
impl<T> CloneArrayIter<T> {
fn new(arr: [T; 3]) -> Self {
Self { arr, idx: 0 }
}
}
impl<T> Iterator for CloneArrayIter<T>
where
T: Clone,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.idx < 3 {
let value = self.arr[self.idx].clone();
self.idx += 1;
Some(value)
} else {
None
}
}
}
And use it as .flat_map(|i| CloneArrayIter::new(get_foo(i)))
I'd probably just use an ArrayVec
from arrayvec though:
extern crate arrayvec;
use arrayvec::ArrayVec;
fn main() {
let foo_selectors = vec![0, 1];
let foos: Vec<_> = foo_selectors
.into_iter()
.flat_map(|i| get_foo(i))
.collect();
println!("{:?}", foos);
}
fn get_foo(i: u8) -> ArrayVec<[u8; 3]> {
if i % 2 == 0 { [1, 2, 3] } else { [4, 5, 6] }.into()
}
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With