- Feature Name: Trait alias別名
- Start Date: 2016-08-31
- RFC PR: rust-lang/rfcs#1733
- Rust Issue: rust-lang/rust#41517
Summary
Traits can be aliased with the trait TraitAlias = …;
construct.+
traits and lifetimes. Type parameters
Motivation
First motivation: impl
Sometimes, some traits are defined
It’s not uncommon to do that in generic crates and implementT
template parameter
Users who want to use that crate will have to export both the trait Foo
from the generic crate and the backend singleton type from the backend crate. Instead, we would like to be able to leave the backend singleton type hidden in the crate. The first shot would be to create a new trait for our backend:
If you try to pass an object that implementsFoo<Backend>
, that won’t work, because it doesn’t implementFooBackend
. However, we can make it work with the following universal impl
:
With that, it’s now possible to pass an object that implementsFoo<Backend>
to a function expecting a FooBackend
. However, what about impl blocks? What happens if we implementFooBackend
? Well, we cannot, because the trait explicitely states that we need to implementFoo<Backend>
. We hit a problem here. The problem is that even though there’s a compatibilitytrait bound
level between Foo<Backend>
and FooBackend
, there’s none at the impl
level, so all we’re left with is implementingFoo<Backend>
– that will also provide an implementationFooBackend
because of the universal implementation
Second example: ergonomic collections and scrapping boilerplate
Another example is associated
It would be nice to be able to create a few aliasesService
.
The trait above is a http service trait which only the associatedFuture
is left to be implemented.Service
trait into use sites – trait bounds,
Detailed design設計(する)
Syntax文法
Declaration宣言
The syntax
Trait aliasing+
construct:
Optionally, if needed, one can provide a where
clause
Furthermore, it’s possible to use only the where
clause
It’s also possible to partially bind associated
This would leave IntoIntIterator
with a free parameterIntoIter
, and it should be bind the same way associated
A trait alias
Specifically,
ATTRIBUTE* VISIBILITY? trait IDENTIFIER(<GENERIC_PARAMS>)? = GENERIC_BOUNDS (where PREDICATES)?;
GENERIC_BOUNDS
is a list+
, the same as the current syntaxPREDICATES
is a comma-separated listwhere
clause.GENERIC_PARAMS
is a comma-separated list
Use semantics
You cannot directlyimpl
a trait alias,
It is an error to attempt to override a previously specified
When using a trait alias
- it contains含むan object safe trait, optionally a lifetime, and zero or more of these other bounds:制限する、結び付けられて
Send
,Sync
(that is,trait Show = Display + Debug;
would not be object safe); - all the associated関連付けられたtypes of the trait need to be specified;特定する、指定する、規定する
- the
where
clause,句、節if present,あるonly contains含むbounds制限する、結び付けられてonSelf
.
Some examples:
The lines marked with (*)
assume
Ambiguous constraints制約
If there are multiple
Teaching
Traits are obviously a huge prerequisite. Traits aliases
Conceptually, a trait alias
- as a bound制限する、結び付けられて: exactly正確にlike a trait, a trait alias別名can be used to constraint制約a type (type parameters仮引数list,リスト、列挙するwhere-clause)
- as a trait object: same thing as with a trait, a trait alias別名can be used as a trait object if it fits object safety restrictions制限(see above in the semantics section)
- in an
impl Trait
Examples should be showed for all of the three cases above:
As a bound制限する、結び付けられて
As a trait object
In an impl Trait
As shown above, a trait alias
Drawbacks
-
Adds another construct
作る、構成体to the language.言語 -
The syntax
文法trait TraitAlias = Trait
requires lookahead in the parser to disambiguate a trait from a trait alias.別名
Alternatives代わりのもの、選択肢
Should we use type
as the keyword instead of trait
?
type Foo = Bar;
already creates an aliasFoo
that can be used as a trait object.
If we used type
for the keyword, this would imply that Foo
could also be used as a boundtrait
as proposed in the body of the RFC, then type Foo = Bar;
and trait Foo = Bar;
both create an alias
However, this mixes the concepts of types and traits, which are different, and allowstype Foo = Rc<i32> + f32;
to parse.
Supertraits & universal impl
It’s possible to create a new trait that derives the trait to alias,impl
This works for trait objects and trait boundsFooFakeAlias
directlyFoo
first – hence, you don’t really need FooFakeAlias
if you can implementFoo
.
There’s currently no alternative
ConstraintKinds
SimilarContraintKinds
, we could declareconstraint
, used as fn quux<T>(...) where Foo<T> { ... }
(i.e. direct substitution). Trait object usage is unclear.
Syntax文法 for sole where
clause.句、節
The current RFC specifieswhere
clause
This is one of many syntaxes
trait DebugDefault where Self: Debug + Default;
(which has been consideredみなす、考慮するand discarded捨てるbecause it might look too much like a new trait definition)trait DebugDefault = _ where Self: Debug + Default;
(which was consideredみなす、考慮するand then removed because it is technically unnecessary)trait DebugDefault = Self where Self: Debug + Default;
(analogous to previous前のcase but not formally discussed)
Unresolved questions
Trait alias別名 containing含む only lifetimes
This is annoying. Consider:
Such an aliastrait
keyword to define
If we chose another keyword, like constraint
, I feel less concerned and it would open furtherConstraintKinds
alternative
Which bounds制限する、結び付けられて need to be repeated when using a trait alias?
RFC 1927 intends to change the rules here for traits, and we likely want to have the rules for trait aliases
The constraint
alternative
What about bounds制限する、結び付けられて on type variable変数、ストレージ declaration宣言 in the trait alias?
PartialEq
has no super-trait Bar
, but we’re adding
Issue 21903 explains the same problem for type aliasing.
Note: what about the following proposal below?
When using a trait alias
Here, T
adds a Bar
bound.
Currently, we don’t have a proper understanding of that situation, because we’re addingBar
boundT
must fulfil in order
BarPrecond
would require the use-site code to fulfil the constraint,
BarImplic
would give us T: Bar
:
BarImpossible
wouldn’t compile because we try to express a pre-condition and an implication for the same bound