Can I extend an iterator with values created inside a function?
Yes:
fn example<'a>(input: impl Iterator<Item = i32> + 'a) -> impl Iterator<Item = i32> + 'a {
Some(42).into_iter().chain(input).chain(Some(99))
}
fn main() {
for i in example(vec![1, 2, 3].into_iter()) {
println!("{}", i);
}
}
I'm looking for a way to turn once(head)
into an iterator of &A
Take a reference to the value:
iter::once(&head)
Is it possible to get [this specific code] to work?
No. The compiler will even tell you that:
error[E0515]: cannot return value referencing local variable `head`
--> src/lib.rs:10:5
|
10 | iter::once(&head).chain(xs.iter()).scan(accum, |accum, x| {
| ^ ----- `head` is borrowed here
| _____|
| |
11 | | *accum += x.0;
12 | | Some(B(*accum))
13 | | })
| |______^ returns a value referencing data owned by the current function
See also:
Is it possible to get [something close to this code] to work?
Maybe. Since scan
starts with the accumulator value, you can just use that instead of sticking it onto the iterator:
fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
xs.iter().scan(xs.len(), |accum, x| {
*accum += x.0;
Some(B(*accum))
})
}
This would mean that the resulting iterator has one less item. If that's acceptable depends on your usage.
A more complicated solution is to have an enum that represents either a borrowed value or an owned value. You can then create an iterator of these enums from the input and the local value. The ownership of the local value is transferred to the returned iterator:
struct A(usize);
struct B(usize);
use std::iter;
// `A` doesn't implement `Clone`; if it did, use `Cow`
enum OwnedOrBorrowed<'a, T> {
Owned(T),
Borrowed(&'a T),
}
impl<'a, T> std::ops::Deref for OwnedOrBorrowed<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
OwnedOrBorrowed::Owned(t) => t,
OwnedOrBorrowed::Borrowed(t) => t,
}
}
}
fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
let accum = 0;
let head = OwnedOrBorrowed::Owned(A(xs.len()));
let borrowed = xs.iter().map(OwnedOrBorrowed::Borrowed);
iter::once(head).chain(borrowed).scan(accum, |accum, x| {
*accum += x.0;
Some(B(*accum))
})
}
This isn't free — every invocation of scan
's closure will perform conditional logic to test if the value is owned or borrowed.
See also: