Chaining iterators when using impl Iterator

Is there a way to make a non-consuming iterator that I can chain together.

I’ve got a function that consumes an iterator and returns a new iterator.

fn ema<I>(mut i: I, period: usize) -> impl Iterator<Item = f32> + Clone 
    I: Iterator<Item = f32> + Clone,

Calling this with

    let prices = Vec::with_capacity(10000);

But prices is consumed. So I created a non-consuming version

fn ema<'a,I:'a>(i: &I, period: usize) -> impl Iterator<Item = f32> + Clone +'a 
    I: Iterator<Item = &'a f32> + Clone,

But I can’t figure out how to chain them, as I get an error:-

error[E0271]: type mismatch resolving `<impl std::clone::Clone+std::iter::Iterator as std::iter::Iterator>::Item == &f32`
   --> driver\src\
215 |         let double_ema = ema(&ema(prices.iter().by_ref(), 20), 15);
    |                         ^^^ expected f32, found &f32
    = note: expected type `f32`
               found type `&f32`

I can fix it by creating an intermediate vec but that seems wasteful allocation

let ema1 = ema(prices.iter().by_ref(),15).collect::<Vec<_>>();
let ema2 = ema(ema1.iter().by_ref(),15).collect::<Vec<_>>();

Is there an idiom I’m missing?

prices isn’t consumed with iter(), but it does give an iterator that yields references. However, you can call prices.iter().cloned() to get an iterator that yields values.

1 Like

So the mut approach is the correct idiom

It’s a fine approach for cheap Clone types. If you wanted references, you can do:

fn ema<'a, I>(mut i: I, period: usize) -> impl Iterator<Item = &'a f32>
    I: Iterator<Item = &'a f32>,

You don’t need the cloned() call for this variant but now you’re yielding refs as well. There’re ways to abstract this as well but I’d just stick to cloned() here.

I tried to follow this, but I get an (expected) lifetime error

fn ema<'a, I: 'a>(mut i: I, period: usize) -> impl Iterator<Item = &'a f32> + Clone + 'a
    I: Iterator<Item = &'a f32> + Clone,
    let alpha = (2 / (period + 1)) as f32;
    let ema0 = i.by_ref().take(1).next().unwrap();
    i.scan(ema0, move |ema_prev, price| {
        *ema_prev = &(*ema_prev + alpha * (price - *ema_prev));

error[E0597]: borrowed value does not live long enough
   --> src\
102 |         *ema_prev = &(*ema_prev + alpha * (price - *ema_prev));
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value only lives until here
    |                      |
    |                      temporary value does not live long enough
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 95:8...

I think I’m missing something? To add, the value approach does work, but will become expensive once I have larger objects

You can abstract over owned vs borrowed, eg like this

Thank you, that does work.

This seems to be saying that I can be prices asf32 or as &f32, and we abstract over either. Fair enough. What I return is a f32 that could be borrowed if chained to another function, though in this instance, returns an iterator yielding values in this case.

I didn’t even know about std::Borrow before now.