Fn trait type parameters; Expected type parameter, found struct

Why function foo doesn't work?

type PrintFn<I> = Box<dyn Fn(I)>;

fn print<I>() -> PrintFn<I>
where
    I: IntoIterator<Item = String>,
{
    Box::new(|iter| {
        for s in iter.into_iter() {
            println!("{} ", s);
        }
    })
}

fn foo<I>(print_fn: PrintFn<I>)
where
    I: IntoIterator<Item = String>,
{
    let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
    print_fn(v.into_iter().skip(1)); // it doesn't work
}

fn main() {
    let print_fn = print();
    
    let v = vec!["1".to_string(), "2".to_string(), "3".to_string()];
    print_fn(v.into_iter().skip(1)); // it works
}

playground

You are mixing up "for all iterators I" with "for some specific kind of iterator I".

What if I decide to call foo<std::vec::IntoIter<String>>? Then you have a function that takes an IntoIter<String>, but you tried to call it with an Skip<IntoIter<String>>, which is a different type. Hence a type mismatch.

This would compile:

fn foo(print_fn: PrintFn<Skip<IntoIter<String>>>) {
    let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
    print_fn(v.into_iter().skip(1)); // it doesn't work
}

It's not really possible to write what you want with closures, because there's actually a different print function for each choice of I. It would be possible with custom structs for functions though.

2 Likes

If you make I a type parameter, then the caller can choose it – it can be any type that satisfies the trait bounds (none in your case). This means that you can't just equate it to another type inside the body of the generic function – the caller might have substituted it for an entirely different type.

2 Likes