Parameter that borrows an `IntoIterator`?

Is there a way to make the following code work (other than accepting an iterator)?

pub fn print_keys<K>(keys: &K,
) where
    K: IntoIterator<Item=String> {
        for key in keys {
            // ^ the trait `Iterator` is not implemented for `&K`
            println!("{key}");
        }
}
pub fn print_keys<'a, K>(keys: &'a K)
where
    &'a K: IntoIterator<Item = &'a String>
{
        for key in keys {
            println!("{key}");
        }
}

This matches how most iterators work; you don't have to restrict to a reference, though (i.e., passing K by-value and adding the exact same bound to K rather than &'a K should work identically and be more lenient).

3 Likes

Passing a reference &K does not make much sense if you want to use the IntoIterator::into_iter() method on K. into_iter takes ownership of your instance of K, creating a type that implements Iterator from it. Passing keys by value instead of by reference would work just fine:

pub fn print_keys<K>(keys: K)
where
    K: IntoIterator<Item = String>,
{
    for key in keys {
        println!("{key}");
    }
}

Playground.

3 Likes

Thanks!

What’s the mechanism that makes this possible? Deref? I couldn’t find anything in Rust’s source code.

References are full types that can have implementations, meet trait bounds, etc. So this works fine:

pub fn print_keys<'a, K>(keys: /*&'a*/ K)
where
    /*&'a*/ K: IntoIterator<Item = &'a String>
{
        for key in keys {
            println!("{key}");
        }
}

And now I could pass in a Vec<&String> or a &Vec<String> since both meet the bound.

(Perhaps see this misconception.)

3 Likes

No, there's no "mechanism", nor does it need any particular "making possible". A type variable can stand in for any type. A reference is a type. Thus, a type variable can stand in for a reference type.

2 Likes

That makes sense to me. I was wondering why and how adding a & to vec in the following code affects IntoIterator::Item:

let vec = Vec::<String>::new();
let mut iter = (&vec).into_iter();
let elem: Option<&String> = iter.next();

And I found the answer – IntoIterator is implemented multiple times:

  • For Vec<T, A>
  • For &'a Vec<T, A>
  • For &'a mut Vec<T, A>
1 Like

Ah, yes, so that's what you are asking. References to collections implement IntoIterator so that you can do this:

for item_ref in &the_coll { … }

instead of this:

for item_ref in the_coll.iter() { … }

and also for the sake of generics, as you observed.

(However, this is not related to the fact that you can either require an explicit reference or not in your function taking an iterable.)

3 Likes

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.