I want to be able to have a function that creates an Iterator from chaining a few combinators together, but I can't figure out how to get it to work without the compiler complaining about an intermediate iterator being dropped after the function returns. I have gotten around it by returning a Vec of the appropriate length instead of the Iterator, but I have recently hit a bottleneck where it allocates too much memory up front for what I'm doing. Here's the original Vec implementation:
/// Makes a sequence of numbers like this:
///
/// 1: [1, 0, -1, 0, 1, 0, -1, 0]
/// 2: [0, 1, 1, 0, 0, -1, -1, 0]
/// 3: [0, 0, 1, 1, 1, 0, 0, 0]
/// ...
pub fn base_pattern(ith_element: usize, len: usize) -> Vec<i32> {
let base: Vec<i32> = vec![0, 1, 0, -1];
let expanded: Vec<i32> = base
.iter()
.map(|e| {
let mut v = Vec::new();
v.resize(ith_element as usize, e);
v
})
.flat_map(|e| e.clone())
.cloned()
.collect();
expanded.iter().cycle().skip(1).take(len).cloned().collect()
}
I thought I could return an impl Iterator<Item = i32> and have the sequence of numbers be lazily generated as they needed like this:
pub fn base_pattern(ith_element: usize, len: usize) -> impl Iterator<Item = i32> {
let base: Vec<i32> = vec![0, 1, 0, -1];
let expanded: Vec<i32> = base
.iter()
.map(|e| {
let mut v = Vec::new();
v.resize(ith_element as usize, e);
v
})
.flat_map(|e| e.clone())
.cloned()
.collect();
expanded.iter().cycle().skip(1).cloned()
}
However, expanded will be dropped before the iterator is returned:
error[E0597]: `expanded` does not live long enough
--> src/lib.rs:15:5
|
3 | pub fn base_pattern(ith_element: usize, len: usize) -> impl Iterator<Item = i32> {
| ------------------------- opaque type requires that `expanded` is borrowed for `'static`
...
15 | expanded.iter().cycle().skip(1).cloned()
| ^^^^^^^^ borrowed value does not live long enough
16 | }
| - `expanded` dropped here while still borrowed
When I lookup rustc --explain E0597 I get an explanation that suggests I 'just remove the scope' so I believe my design of this must be all wrong, as I have painted myself into a borrow-checked corner.
How can I keep the implementation of this iterator separate from the rest of my code without inlining this where it's used?