I was reading the RFC on \"expanding\" impl Trait when I came upon the following:
By contrast, a programmer who first learned:
fn take_iter(t: i
The RFC defines the terms multiple times in multiple manners:
between existential types (where the callee chooses the type) and universal types (where the caller chooses)
There's been a lot of discussion around universals vs. existentials (in today's Rust, generics vs impl Trait).
Universal quantification, i.e. "for any type T", i.e. "caller chooses". This is how generics work today. When you write
fn foo<T>(t: T)
, you're saying that the function will work for any choice ofT
, and leaving it to your caller to choose theT
.Existential quantification, i.e. "for some type T", i.e. "callee chooses". This is how
impl Trait
works today (which is in return position only). When you writefn foo() -> impl Iterator
, you're saying that the function will produce some typeT
that implementsIterator
, but the caller is not allowed to assume anything else about that type.
TL;DR:
fn take_iter(t: impl Iterator)
— the person calling take_iter
picks the concrete type. The function has to work for the entire "universe" of types that implement the trait.
fn give_iter() -> impl Iterator
— the implementation of give_iter
picks the concrete type. There is some type which "exists" and implements the trait that will be returned by the function.