question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Positional Associated Types

See original GitHub issue

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.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:17
  • Comments:11 (10 by maintainers)

github_iconTop GitHub Comments

11reactions
nikomatsakiscommented, Dec 6, 2021

While I have felt the pain of wishing for a shorthand like Iterator<u32>, I am concerned that this change is going to make the distinction between associated types and generic parameters harder for people to understand. I would like to think it’s possible for us to empirically evaluate this to some degree, though I also do feel that prototyping and using it for a while is a good way to gain experience.

(That said, I am also skeptical of the motivation, or at least one motivation I have heard: specifically, I don’t think that having generators have a return type like -> impl Iterator<Item = u32> is necessarily a good idea, given the precedent set by async fn (and, in fact, generators share the property that they capture all their arguments, which to me was a crucial reason for async fn to have the type they do).)

I don’t see this concern as blocking exploration, but I wanted to note it down, and I would expect to spend some time discussing it before we moved to any sort of RFC here.

3reactions
pnkfelixcommented, Dec 28, 2021

Summary of concerns gathered from lang team meeting on 2021-11-16 and from the zulip thread:

  • sem. ver issue: supporting PAT on a trait (via the given syntaxTrait<Assoc>) implies it is a breaking change to ever add a defaulted generic param.
    • Concern: So, even though PAT’s are opt-in, there is a huge back compat promise. How much use is this expected to get, and does that justify a language change (as opposed to some alternative solution; some are outlined below)
  • the proposal says it is unclear to incoming Rust programmers why they have to write Iterator<Item=f32> instead of Iterator<f32>, and attempts to address that by unifying the two syntaxes.
    • Concern: We should not assume that conflating two different things, or hiding the difference between them, improves learning curves.
    • Counterpoint: Forcing people to pay attention to a difference like this is not necessarily a win either.
  • Concern (admittedly vague): in future scenarios, the distinction between generics and associated types may become important in new ways.

However, it is important to stress that that this is a proposal to solve a problem, not a commitment to a specific solution. The syntax could change, which might address some of the concerns above. In that spirit, here is a list of alternative solutions suggested in Zulip thread:

Read more comments on GitHub >

github_iconTop Results From Across the Web

Deep dive into Associated types in Swift - SwiftCodeStreet
In simple terms, the Associated types is used to make protocol generic. Associated type is dependent upon these two concepts in Swift ...
Read more >
A Protocol-Oriented Approach to Associated Types and Self ...
An associated type is a placeholder in a protocol for a type that is used as part of the protocol. Any type that...
Read more >
The Complete Guide to Patient Positioning - STERIS
Jackknife position, also known as Kraske, is similar to Knee-Chest or Kneeling positions and is often used for colorectal surgeries. This type ......
Read more >
Orthotic treatment of positional brachycephaly associated with ...
This treatment offers too high levels of satisfaction to parents and brings these children closer to normal indices. Keywords: Osteogenesis imperfecta; infant; ...
Read more >
Important Concepts In Qt Quick - Positioning
Visual items in QML can be positioned in a variety of ways. The most important positioning-related concept is that of anchoring, a form...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found