Pre-RFC: Elide type or lifetime parameters in impl blocks


#1
  • Feature Name: elide_unused_params_in_impl
  • Start Date: 2016-01-11
  • RFC PR: (leave this empty)
  • Rust Issue: (leave this empty)

Summary

Allow impl Trait for Struct<'a, 'b, X, Y> to be written as impl Trait for Struct in case the impl never references its lifetime and/or type parameters.

Motivation

Making code easier to read

impl<'a, 'b, X, Y> Drop for Puppy<'a, 'b, X, Y> {
    fn drop(&mut self) { println!("Vooof!"); }
}

In the case above, <'a, 'b, X, Y> is just visual noise. We should be able to instead write:

impl Drop for Puppy {
    fn drop(&mut self) { println!("Vooof!"); }
}

Making code easier to refactor

When a lifetime and/or a type parameter is added or removed from a struct, usually a lot of places also need to be updated. With this change, fewer places need to be updated, making it easier to refactor the code.

Detailed design

Step 1

For impl blocks such as impl Struct, impl Enum, impl Trait for Struct, and impl Trait for Enum, one can skip either all lifetime parameters, all type parameters, or both, provided that there is no reference to those lifetime and/or type parameters inside the impl.

Notice the all or nothing rule - a Puppy<'a, 'b, X, Y> can be shortened to Puppy<X, Y>, Puppy<'a, 'b> and Puppy, but no other combinations are possible. This is to make sure parameters do not get mixed up with each other.

If the struct/enum has constraints, such as: struct Puppy<X: Eq, Y: Clone>, those constraints will follow along in the impl, so impl Drop for Puppy will be short for impl<X: Eq, Y: Clone> Drop for Puppy<X, Y>. (In this case, writing e g impl<X: Copy, Y: Clone> Drop for Puppy<X, Y> will fail compilation because Copy does not imply Eq.)

Step 2

Inside impl blocks where the type is referenced, that reference will assume having the same lifetime/type parameters as its impl. This will make the following code valid, which it is not in step 1:

enum Puppy<'a, X> {
    Cute(X),
    VeryCute(& 'a str),
}

impl<X> Puppy<X> {
    fn new_cute(x: X) -> Puppy<X> { Puppy::Cute(x) }
}

Above, the 'a will be elided and assumed to be same as the impl, i e it’s short for:

impl<'a, X> Puppy<'a, X> {
    fn new_cute(x: X) -> Puppy<'a, X> { Puppy::Cute(x) }
}

Step 3

Once this is done, it seems natural to also extend this to cover free functions, i e,

fn bark<'a, 'b, X, Y>(p: &Puppy<'a, 'b, X, Y>) { println!("Woof! Woof!"); }

can be shortened to:

fn bark(p: &Puppy) { println!("Woof! Woof!"); }

Drawbacks

Every new something adds complexity to the language. I consider the added complexity in this case to be very minor, though.

Feel free to come up with more drawbacks :slight_smile:

Alternatives

Stay with status quo.

Unresolved questions

Traits can also have type and lifetime parameters. This RFC does not cover them, it can be done at some later stage.


Disclaimer: No puppies were dropped, or had their lifetimes shortened, while this Pre-RFC was written.