7

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    It was chosen to work this way because these are the most common use cases. There are also cases where an existential argument type or a universal return type would be useful, but these are relatively rare so the RFC chose not to implement them and keep things simple. – interjay Dec 21 '17 at 17:36
  • @interjay A common example of a universally quantified return type is `Into::into()`. The caller supplies the type that they want to get back (usually by inference). – Peter Hall Jul 29 '18 at 10:12

1 Answers1

15

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 of T, and leaving it to your caller to choose the T.

  • 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 write fn foo() -> impl Iterator, you're saying that the function will produce some type T that implements Iterator, 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.

red75prime
  • 3,733
  • 1
  • 16
  • 22
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366