HRTB Sugar

Now that GATs are close to stabilizing, HRTBs are going to be used much more for associated type bounds:

trait Foo {
    type Output<'a>;
    fn foo(&self) -> Self::Output<'_>;
}

fn foo<F>(foo: F)
where
    F: Foo,
    for<'a> F::Output<'a>: Clone,
{
    // ...
}

What if the elided lifetime could be used as a simpler version of this?

fn foo<F>(foo: F)
where
    F: Foo,
    F::Output<'_>: Clone,
{
    // ...
}

This could also interact nicely with feature(associated_type_bounds):

fn foo<F>(foo: F)
where
    F: Foo<Output<'_>: Clone>,
{
    // ...
}

We'll also need some way to express HRTB bounds with type parameters:

trait Foo {
    type Output<T>;
    fn foo<T>(self) -> Self::Output<T>;
}

fn foo<F>(foo: F)
where
    F: Foo,
    for<T: Clone> F::Output<T>: Clone // <---
{
    // ...
}

And perhaps _ could be used here as well as sugar, applying the bound on the associated type to the elided generic parameter as default behavior:

fn foo<F>(foo: F)
where
    F: Foo,
    F::Output<_>: Clone
    // => for<T: Clone> F::Output<T>: Clone
{
    // ...
}

Again, would work well with feature(associated_type_bounds):

fn foo<F>(foo: F)
where
    F: Foo<Output<_>: Clone>,
{
    // ...
}

Note that neither '_ or _ are allowed in the proposed position, so this would be a non-breaking change.

This idea originally came out of thinking about async function syntax.

This is the only one I disagree with. While this follows the same logic as #[derive], I'd personally more expect this syntax to mean

fn foo<F>(foo: F)
where
    F: Foo,
    F::Output<_>: Clone
    // => for<T> F::Output<T>: Clone
{
    // ...
}

with only the implied bounds (once those are stable).

Due to the two valid interpretations (#[derive] style or impl style), I'd lean on not doing this one.

I'm generally green on allowing the elided lifetime for this purpose, though we should be careful that it cannot be confused with the original elided lifetime (from &self or the single input lifetime).

3 Likes

Maybe technically it wouldn't, but conceptually it's very different from what elided lifetimes mean currently. Lifetime elision is already problematic enough. Let's definitely not override identical syntax to mean implicit HRTBs, too.

1 Like

What implied bounds would there be for HRTBs?

Another take on a potential meaning for '_ in associated types is

I think the "associated types are outputs" from that is correct, though that RFC didn't address GATs.

1 Like

I would personnaly prefer F::Output<T: Clone>: Clone. With the associated_type_bounds version being F: Foo<Output<T: Clone>: Clone>

Maybe F::Output<_: Clone>: Clone?

Yes.

1 Like

I haven't looked terribly closely or recently at this, bugs/features for it, or what would be involved; but last time I looked HRTB's weren't allowed on aliases... I'd probably rather see HRTBS on aliases work over some form of syntactic sugar personally... if possible.

Can you explain what you mean by this? How would HRTBs on type? aliases help here?

I can't find the example where I had tried to use HRTB's, but in general all bounds (even lower ranked) are currently ignored on type aliases, they do not currently help at all, because warning: bounds on generic parameters are not enforced in type aliases

But here is one which shows where bounds in general don't work on aliases.

type Foo<A: Copy> = fn(A) -> A;

fn foo(f: Foo<()>) -> () {
    f(())
}

Does that help explain what I meant?

Ah, I see, but how does that fit in with higher ranked GAT bounds?

Honestly I'm just not a fan of implicit or implied syntax for most anything. My hope was that part of the problem with bounds is they tend to get repeated in places (GAT's probably less so however bounds are bounds higher ranged or otherwise).

Anyhow the hope was that alias bounds would just reduce the need for sugar all together, since you could just use the short alias rather than repeating the entire bound everywhere.

As an outsider to the Rust community that is noneless interested in language design discussions, the acronyms here took some deciphering. (I would encourage people to expand them in the first post or so, to make it easier to follow out of context.)

I believe that:

3 Likes