Tentative: impl chaining
Nobody has claimed this yet.
- Dominant language
- Markdown
- Stars
- 6.6k
- Forks
- 1.7k
- Avg merge
- 16h 14m
- Merged PRs (30d)
- 1
Description
Summary
'impl chaining' allows declaring impls on items without repeating the type variable declarations.
Motivation
Generics with bounds can become rather verbose. Consider this example:
use std::ops::Index;
use std::marker::PhantomData;
pub struct TypedArray<IdxIn, IdxOut, T>
where
T: Index<IdxOut>,
IdxIn: Into<IdxOut>,
{
val: T,
indexers: PhantomData<(IdxIn, IdxOut)>,
}
// 'free impl'
impl<IdxIn, IdxOut, T> Index<IdxIn> for TypedArray<IdxIn, IdxOut, T>
where
T: Index<IdxOut>,
IdxIn: Into<IdxOut>,
{
type Output = T::Output;
fn index(&self, index: IdxIn) -> &Self::Output {
&self.val[index.into()]
}
}
This proposes some syntax to sop up the un-DRY repetition:
use std::ops::Index;
use std::marker::PhantomData;
pub struct TypedArray<IdxIn, IdxOut, T>
where
T: Index<IdxOut>,
IdxIn: Into<IdxOut>,
{
val: T,
indexers: PhantomData<(IdxIn, IdxOut)>,
}
// 'chained impl'
in impl Index<IdxIn> {
type Output = T::Output;
fn index(&self, index: IdxIn) -> &Self::Output {
&self.val[index.into()]
}
}
Detailed design
The feature is called "impl chaining", and the in impl item is a "linked impl". The original impls are hereby dubbed "free impl".
Semantics
As the compiler parses the module or block expression, the type variables of the most recently defined enum, struct, or type alias is kept in memory.
(eg, Option<Scope>)
Certain items reset this state, and some items do not affect it.
Functions and modules reset it.
Imports do not reset it ???
Variable, constants, and statics reset also?
A linked impl can of course follow another linked impl.
They can also introduce new type variables, and add additional bounds,
but these changes are scoped only to that specific link, and is not visible to any following links.
If the user wants to add a bound used by multiple impl links,
they can set up the desired scope by adding a (possibly non-empty!) free impl.
Syntax
I think with impl would look the most natural, however this (probably) has ambiguities when parsing block expressions, and impl non_keyword would break compatibility. in and for are the only keywords that could possibly make any sense for impl chaining, so that gives these options:
in implimpl infor implimpl for
How We Teach This
What names and terminology work best for these concepts and why?
How is this idea best presented—as a continuation of existing Rust patterns, or as a wholly new one?
Would the acceptance of this proposal change how Rust is taught to new users at any level?
How should this feature be introduced and taught to existing Rust users?
What additions or changes to the Rust Reference, _The Rust Programming Language_, and/or _Rust by Example_ does it entail?
Drawbacks
Old code would like to be changed to use this.
It makes the language more complicated.
This might encourage sloppier code. It could encourage proliferation of unnecessary bounds, because a struct can have laxer bounds than an impl.
It might encourage re-arranging the file in less-than-ideal ways.
The example doesn't seem very convincing to me. Maybe there there aren't any examples that are really all that convincing.
Maybe the pain of it will encourage the production of Very Fancy Rust IDEs.
I was surprised to learn that you can do a free impl on a type alias.
Maybe other people would be surprised by this and inadvertently chain an impl to a type alias.
Alternatives
What other designs have been considered? What is the impact of not doing this?
- Leave it be.
- Allow functions to be declared in a struct/enum body:
struct Thing {
field: i32,
fn new() -> Self {
Thing { field: 5 }
}
}
This is not as powerful, but it is simple & what other languages do.
Unresolved questions
What parts of the design are still TBD?
Better names?
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the issue's Detailed design, especially the Semantics and Syntax sections, then review Alternatives and Unresolved questions. Done would require settled syntax and semantics for impl chaining; this payload identifies no implementation files or tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100