I am trying to write a function that takes any iterable to a generic type as input and loops over the elements. Here is a working example
pub fn test1<'a, IterableT, ItemT>(nodes: &'a IterableT)
where
&'a IterableT: IntoIterator<Item = &'a ItemT>,
ItemT: 'a + Debug
{
for x in nodes {
println!("consuming or iterating: {:?}!", x);
}
}
This works with reference types. I struggle to implement a version that works with both regular types and references. The closest I have got it the following
pub fn test1<IterableT>(nodes: IterableT)
where
IterableT: IntoIterator,
IterableT::Item: Debug
{
for x in nodes {
println!("consuming or iterating: {:?}!", x);
}
}
This works as long as the reference version of the type I am using implements IntoIterator (for example vec
, &vec
and &mut vec
). The problem is that I can't reintroduce the ItemT
generic since then the underlying Item
is narrowed down to one of Item
or &Item
pub fn test1<IterableT, ItemT>(nodes: IterableT)
where
IterableT: IntoIterator<Item = ItemT>, // alternatively IntoIterator<Item = &ItemT>
ItemT: Debug
{
for x in nodes {
println!("consuming or iterating: {:?}!", x);
}
}
Depending on whether I use IntoIterator<Item = ItemT>
or IntoIterator<Item = &ItemT>
, this works with vec
or &vec
but not both. A hacky workaround is to specify IterableT::Item: Into<ItemT>
, but this only works if the underlying type implements the Into traits (so this will work with built-in types like i32, i64, but not for some custom made struct).
Is there a way to indicate that Item
can either be ItemT
or &ItemT
in the where
clause? The reason I need to introduce ItemT
is because it is a generic variable of the parent struct that this function is a part of. I need to accept iterators specifically with that type.
You can use std::borrow::Borrow
to treat T
and &T
the same.
use std::{borrow::Borrow, fmt::Debug};
fn debug_iterator<I, T>(it: I)
where
I: IntoIterator,
I::Item: Borrow<T>,
T: Debug + ?Sized,
{
for item in it.into_iter() {
println!(" item: {:?}", item.borrow());
}
}
In action:
fn main() {
let v1 = vec![String::from("hello"), String::from("world")];
println!("By reference:");
debug_iterator::<_, String>(v1.iter());
println!("By value:");
debug_iterator::<_, String>(v1);
}
Output:
By reference:
item: "hello"
item: "world"
By value:
item: "hello"
item: "world"
Note that because the Borrow
trait has a type parameter, you have to provide the correct type for T
explicitly at the call site; Rust will not be able to infer the type you want.