Alexey S. Larionov
Alexey S. Larionov

Reputation: 7927

IntoIterator as a function argument doesn't accept adapter struct

I want to have a function, that accepts &IntoIterator<Item=u32>, so I could pass to it both &Vec<u32> and iterators' adapter structs (like Map, Filter and any other, which I believe all implement IntoIterator)

So I have a function like

pub fn f<'a, T>(it_src: &'a T) -> u32
where &'a T: IntoIterator<Item = u32> {
    let it = it_src.into_iter();
    let result: u32;
    // more more usage
    result
}

And this is how I tried to use it (same signature, but different name)

pub fn f_with_feature()<'a, T>(it_src: &'a T) -> u32
where &'a T: IntoIterator<Item = u32> {
    let adjusted_values = it_src.into_iter()
        .map(|e| adjust(e));
    f(&adjusted_values)
}

What I've got is an error

 error[E0308]: mismatched types
  --> src\main.rs:14:7
   |
14 |     f(&adjusted_values)
   |       ^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::iter::Map`
   |
   = note: expected type `&T`
              found type `&std::iter::Map<<&T as std::iter::IntoIterator>::IntoIter, [closure@src\main.rs:13:14: 13:27]>`

How is it that Map doesn't match as T?

Also, I've come up with an idea, that passing iterators' adaptors with static dispatch isn't a good idea since each other closure used to generate a Map will create a new function specialization. Though I've seen that static dispatch approach for most of the times is idiomatic in Rust. How to manage this situation?

Upvotes: 3

Views: 2281

Answers (1)

phimuemue
phimuemue

Reputation: 36003

I think you want to have trait bounds on T (and not on &'a T). So I guess you actually want the following:

pub fn f<'a, T>(it_src: &'a T) -> u32
where T: IntoIterator<Item = u32> {
    let it = it_src.into_iter();
    let result: u32 = 1;
    // more more usage
    result
}

pub fn f_with_feature<'a, T>(it_src: &'a T) -> u32
where T: IntoIterator<Item = u32> {
    let adjusted_values = it_src.into_iter()
        .map(|e| adjust(e));
    f(&adjusted_values)
}

Which brings us to the next problem: IntoIterator's into_iter consumes self, which means that you cannot call it_src.into_iter if you only borrow it_src.

So if you really want to use into_iter, you can try this:

pub fn f<T>(it_src: T) -> u32
where T: IntoIterator<Item = u32> {
    let it = it_src.into_iter();
    let result: u32 = 1;
    // more more usage
    result
}

pub fn f_with_feature<T>(it_src: T) -> u32
where T: IntoIterator<Item = u32> {
    let adjusted_values = it_src.into_iter()
        .map(|e| adjust(e));
    f(adjusted_values)
}

The above, however, requires you to move the values into f resp. f_with_feature.

In my experience, just taking an iterator (and doing the conversion at call site if necessary), leads to simple, straightforward solutions:

pub fn f<T>(it_src: T) -> u32
where T: Iterator<Item = u32> {
    let it = it_src.into_iter();
    let result: u32 = 1;
    // more more usage
    result
}

pub fn f_with_feature<T>(it_src: T) -> u32
where T: Iterator<Item = u32> {
    let adjusted_values = it_src.into_iter()
        .map(|e| adjust(e));
    f(adjusted_values)
}

Upvotes: 3

Related Questions