GAT use case analysis

Limitations re. bounds & constraints

  • Bounds on the trait itself, e.g. trait Foo { type Bar<'a>: Baz; }
  • Bounds on the parameters of an associated type, e.g. trait Foo { type Bar<T: >; }

// The following should be equivalent:

trait Foo  {
    type Bar<B: SomeBound>: OtherBound;
}

trait Foo where for<B: SomeBound> Self::Bar<B>: OtherBound {
    type Bar<B> where B: SomeBound;
}

  • Most of our examples don’t use any bounds at all; those that do tend to bound the associated type itself, not its parameter
  • Question for Niko: Which is the problem that chalk is a must-have for? Bounds on B or on Bar?

Motivations

  • Breaking changes for libraries that would want to use GATs; would allow for earlier “1.0” / highly stable APIs
  • Sometimes holding back really essential API work, e.g. being able to abstract over async functions that borrow

Example use cases

Simple lifetime-parametric types

Streaming iterators
Important in async I/O world
  • How important is somewhat TBD, unclear whether this blocks stabilizing a Stream trait or whether we will want two traits

trait Iterator {
    type Item<'a> where Self: 'a;
    fn next<'a>(&'a mut self) -> Self::Item<'a>;
}

Proptest strategies

pub trait ValueTree {
    type Value<'a>: fmt::Debug where Self: 'a;
    fn current<'a>(&'a self) -> Self::Value<'a>;
    fn simplify(&mut self) -> bool;
    fn complicate(&mut self) -> bool;
}
pub trait Strategy: fmt::Debug {
    /// The value tree generated by this `Strategy`.
    type Tree: ValueTree<Value = Self::Value>;

    /// The type of value used by functions under test generated by this Strategy.
    type Value<'a>: fmt::Debug;

    /// Generate a new value tree from the given runner.
    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self>;
}