Skip to content

Positional Associated Types #126

@samsartor

Description

@samsartor

Positional Associated Types

Proposal

Summary and problem statement

It should be possible to abbreviate Future<Output=T> as Future<T>.

Motivation, use-cases, and solution sketches

Many of the most-used traits in the rust programming language specify a single associated type:

  • Iterator<Item=T>
  • Future<Output=T>
  • FromStr<Err=T>
  • Deref<Target=T>
  • Stream<Item=T>
  • and so on

The impl Trait syntax is intended to make instances of such traits easy to pass and return. But this can be quite verbose in practice, because each of these traits must be written with the associated type explicitly spelled out:

fn deltas(iter: impl Iterator<Item=f32>) -> impl Iterator<Item=f32> {
    iter.array_windows().map(|[a, b]| b - a) 
}

Worse, it may not be clear to incoming Rust programmers why they have to write Iterator<Item=f32> instead of Iterator<f32>. After all, the generics of traits like From<T> and types like Option<T> are not named. It can take many months for the difference between generic parameters and associated types to click, and in that time users probably guess at random which they should use for any given trait.

It is worth investigating whether these common cases can be abbreviated:

pub trait Future {
    #[positional]
    type Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

impl Logger {
    pub fn log_outcome(task: impl Future<String>) {
        ...
    }
}

This should also apply to the dyn Future<T> and F: Future<T> syntaxes.

The Iterator<T> and Future<T> traits would almost certainly be the first to have positional associated types (PATs) stabilized. But the feature could also be beneficial for some traits with multiple associated types:

pub trait Try: FromResidual<Self::Residual> {
    #[positional]
    type Output;

    #[positional]
    type Residual;

    fn from_output(output: Self::Output) -> Self;
    fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
}

impl Logger {
    pub fn log_outcome(task: impl Future<impl Try<impl Display, impl Display>>) {
        ...
    }
}

Note this proposal would not immediately include any of the following use cases:

// PATs in trait implementations 
impl Iterator<f32> for MyIterator { }

// Mixing type parameters and PATs
trait MyTrait<T> {
    #[positional]
    type Extra;
}

// Named type parameters
fn get(&self, key: &impl Borrow<Borrowed=str>) { }

Links and related work

Initial people involved

  • Owner: Sam Sartor
  • Liaison: Josh Triplett

What happens now?

This issue is part of the lang-team initiative process. Once this issue is filed, a Zulip topic will be opened for discussion, and the lang-team will review open proposals in its weekly triage meetings. You should receive feedback within a week or two.

This issue is not meant to be used for technical discussion. There is a Zulip stream for that. Use this issue to leave procedural comments, such as volunteering to review, indicating that you second the proposal (or third, etc), or raising a concern that you would like to be addressed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-langfinal-comment-periodThe FCP has started, most (if not all) team members are in agreementmajor-changeMajor change proposalto-announceNot yet announced MCP proposals

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions