Impl type parameter aliases

It would be very useful to be able to to the following:

impl From<I: Option<&CfgState>> for ParserError {
    fn from(item: I) -> Self {
        ParserError::StackEmpty
    }
}

____or____

impl From<I as Option<&CfgState>> for ParserError {
    fn from(item: I) -> Self {
        ParserError::StackEmpty
    }
}

+1, would be particularly handy now that we have impl header lifetime elision.

EDIT

To elaborate, IHLE allowed us to do this:

- impl<'a> Add<u32> for &'a u32 {
+ impl Add<u32> for &u32 {
      type Output = u32;
      fn add(self, rhs: u32) -> u32 { *self + rhs }
  }

With parameter aliases, we’d be able to do

- impl<'a> Add<&'a u32> for u32 {
+ impl Add<RHS @ &u32> for u32 {
      type Output = u32;
-     fn add(self, rhs: &'a u32) -> u32 { self + *rhs }
+     fn add(self, rhs: RHS) -> u32 { self + *rhs }
  }

Which is in some ways even nicer, since it removes more uses of the lifetime than the first change did.

3 Likes

This confused me, my line of thinking was something like this: wait, Option is not a trait, is CfgState a trait?, then why is this not &dyn CfgState?, oh, wait... In other words, this syntax looks ambiguous, especially if mixed with other arguments: impl<A, B> Trait<X: A, Y: B, Z: C>.

Here the order is different from the other place where as is used to name something, as in use fmt::Result as FmtResult.

Some alternatives:

impl From<Option<&CfgState> as I> { /*...*/ }
impl From<I = Option<&CfgState>> { /*...*/ }
impl From<type I = Option<&CfgState>> { /*...*/ }
2 Likes

Another alternative that I think fits better with current generics (this could be combined with any of the previous syntaxes inside the <>)

impl<type I = Option<&CfgState>> From<I> for ... { ... }

namely, the type alias is introduced for the impl block, same as where generic type parameters are introduced, rather than in the application of the From trait constructor.

2 Likes

This seems related to this kind of syntax:

I actually really like thus method and the reasoning for why it should be on the impl block makes even more sense. Plus the syntax is basically the same as type definitions already, just comma separated.

Should I start thinking about writing up an RFC?

The main thing keeping in mind in this design space is that generalizing this feature to allow arbitrary types on the left hand side is the sort of trivially undecidable typechecking problem we try to avoid. That is, you cannot start trying to solve SomeType<T>::Assoc = AnotherType<U>::Assoc without running into trouble.

But just creating a trivially substitutable name for a concrete type is not so impossible. Mainly it’s the problem that every feature runs into: determining the priority for the project, bikeshedding the syntax, and deciding the impact on the user experience.

So currently, type definitions (in the form of type name = old_type;) are already scoped in rust (see example).

So what I imagined was that the following:

impl<type I = i32> From<I> for MyType {
    fn from(item: I) -> Self {
        ...
    }
}

would be functionally equivalent to:

{
    type I = 32;
    impl From<I> for MyType {
        fn from(item: I) -> Self {
            ...
        }
    }
}

This, I feel, can definitely be bike-shed with a macro to start but I think is a useful syntactic addition anyway.

To add to the bikeshed options,

impl From<I @ Option<&CfgState>> { /*...*/ }

Like in patterns.

(I’m actually rather fond of this one, since in a way trait resolution is pattern matching over the type of the parameter here…)

5 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.