问题
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: impl Iterator)
and then tried:fn give_iter() -> impl Iterator
would be successful, without any rigorous understanding that they just transitioned from a universal to an existential.
While I understand universal vs existential from a logic perspective, what makes the first one above universal and the second one existential?
回答1:
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 callingtake_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 ofgive_iter
picks the concrete type. There is some type which "exists" and implements the trait that will be returned by the function.
来源:https://stackoverflow.com/questions/47929824/what-makes-impl-trait-as-an-argument-universal-and-as-a-return-value-existe