Vec and IntoIterator - unexpected type parameter

I've got a program structure that doesn't work, and I'm not sure why. I'll show it with a data structure and these two methods:

#[derive(Debug, Clone)]
struct Data {
    id: i32,
}

fn as_sorted_iterable<'a, I>(iterable: &'a I) -> Vec<Data>
    where &'a I: IntoIterator<Item = &'a Data>,
{
    let mut sorted: Vec<Data> = iterable.into_iter().cloned().collect();

    sorted.sort_by_key(|x| x.id);
    sorted
}

fn print<'a, I>(iterable: &'a I)
    where &'a I: IntoIterator<Item = &'a Data>,
{
    for item in iterable {
        println!("{:?}", item);
    }
}

Now, individually these methods work, and even in main() I can use them together. But watch what happens when I use a middleman function with the same signature as print.

fn sort_and_print<'a, S>(iterable: &'a S)
    where &'a S: IntoIterator<Item = &'a Data>,
{
    print(&iterable);
    // This is fine but

    let sorted = as_sorted_iterable(iterable);
    print(&sorted);
    // This throws an error
}

It doesn't like the as_sorted_iterable + print combo.

error[E0308]: mismatched types
  --> src/main.rs:42:11
   |
35 | fn sort_and_print<'a, S>(iterable: &'a S)
   |                       - this type parameter
...
42 |     print(&sorted);
   |           ^^^^^^^ expected type parameter `S`, found struct `Vec`   |
   = note: expected reference `&S`
              found reference `&Vec<Data>`

Again, I am not sure what the middleman function has to do with it, because I can do the same thing fine in main. Why doesn't the vector satisfy S, anyway?

Playground

It's interesting that the second where clause (which is always satisfied, since it doesn't depend on the generic parameters at all) inverts the problem:

fn sort_and_print<'a, S>(iterable: &'a S)
    where &'a S: IntoIterator<Item = &'a Data>,
    for<'vec> &'vec Vec<Data>: IntoIterator<Item = &'vec Data>
{
    print(&iterable); // breaks - &&S is not IntoIterator

    let sorted = as_sorted_iterable(iterable);
    print(&sorted); // works
}

Might this be another case of this issue (and therefore of this one)?

1 Like

I see. I should say that my purpose in throwing around IntoIterator is to embrace generality, like one might do in C# with IEnumerable. So to see that I have to namedrop Vec anyway seems to defeat the purpose.

It's interesting that this works though.

for<'vec> &'vec Vec<Data>: IntoIterator<Item = &'vec Data>

If this is always satisfied, how does this information help the compiler?

For some reason i don't now the compiler chooses the wrong implementation of the print function however you can use print::<Vec<_>>(&sorted); to explicit choose the right one.

1 Like

By the way, the original code has more explicit &s than it needs. If you let the input type parameters be the references instead of the referents, then the code is more general, more concise, and doesn't need the extra explicit bound though it still needs a for<'a>:

fn as_sorted_iterable<'a, I>(iterable: I) -> Vec<Data>
where
    I: IntoIterator<Item = &'a Data>,
{
    let mut sorted: Vec<Data> = iterable.into_iter().cloned().collect();

    sorted.sort_by_key(|x| x.id);
    sorted
}

fn print<'a, I>(iterable: I)
where
    I: IntoIterator<Item = &'a Data>,
{
    for item in iterable {
        println!("{:?}", item);
    }
}

fn sort_and_print<S>(iterable: S)
where
    for<'s> &'s S: IntoIterator<Item = &'s Data>,
{
    print(&iterable);
    let sorted = as_sorted_iterable(&iterable);
    print(&sorted);
}

Also, you could skip cloning the Datas and build a sorted vector of &'a Data references. Whether that's a good idea depends on the real use cases and whether Data is large enough that avoiding cloning it is worthwhile.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.