2019-02-25 21:04:20 +08:00
|
|
|
|
- Feature Name: `associated_type_defaults`
|
2018-08-28 00:45:56 +08:00
|
|
|
|
- Start Date: 2018-08-27
|
2019-02-25 21:04:20 +08:00
|
|
|
|
- RFC PR: [rust-lang/rfcs#2532](https://github.com/rust-lang/rfcs/pull/2532)
|
|
|
|
|
- Rust Issue: [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661)
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
|
|
|
|
# Summary
|
|
|
|
|
[summary]: #summary
|
|
|
|
|
|
|
|
|
|
[RFC 192]: https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#defaults
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[Resolve][changes] the design of associated type defaults,
|
|
|
|
|
first introduced in [RFC 192],
|
|
|
|
|
such that provided methods and other items may not assume type defaults.
|
|
|
|
|
This applies equally to `default` with respect to specialization.
|
|
|
|
|
Finally, `dyn Trait` will assume provided defaults and allow those to be elided.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
|
|
|
|
# Motivation
|
|
|
|
|
[motivation]: #motivation
|
|
|
|
|
|
|
|
|
|
As discussed in the [background] and mentioned in the [summary],
|
|
|
|
|
associated type defaults were introduced in [RFC 192].
|
|
|
|
|
These defaults are valuable for a few reasons:
|
|
|
|
|
|
|
|
|
|
1. You can already provide defaults for `const`s and `fn`s.
|
|
|
|
|
Allowing `type`s to have defaults adds consistency and uniformity
|
|
|
|
|
to the language, thereby reducing surprises for users.
|
|
|
|
|
|
|
|
|
|
2. Associated `type` defaults in `trait`s simplify the grammar,
|
|
|
|
|
allowing the grammar of `trait`s them to be more in line with
|
|
|
|
|
the grammar of `impl`s. In addition, this brings `trait`s more in line
|
|
|
|
|
with `type` aliases.
|
|
|
|
|
|
|
|
|
|
The following points were also noted in [RFC 192], but we expand upon them here:
|
|
|
|
|
|
|
|
|
|
3. Most notably, type defaults allow you to provide more ergonomic APIs.
|
|
|
|
|
|
|
|
|
|
[proptest]: https://altsysrq.github.io/rustdoc/proptest/latest/proptest/arbitrary/trait.Arbitrary.html
|
|
|
|
|
|
2018-08-27 06:05:19 +08:00
|
|
|
|
For example, we could change [proptest]'s API to be:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Arbitrary: Sized + fmt::Debug {
|
|
|
|
|
type Parameters: Default = ();
|
2019-06-10 04:00:24 +08:00
|
|
|
|
|
2018-08-24 01:08:55 +08:00
|
|
|
|
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy;
|
|
|
|
|
|
|
|
|
|
fn arbitrary() -> Self::Strategy {
|
|
|
|
|
Self::arbitrary_with(Default::default())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Strategy: Strategy<Value = Self>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Being able to say that the default of `Parameters` is `()` means that users,
|
|
|
|
|
who are not interested in this further detail, may simply ignore specifying
|
2018-08-24 01:08:55 +08:00
|
|
|
|
`Parameters`.
|
|
|
|
|
|
|
|
|
|
The inability of having defaults results in an inability to provide APIs
|
|
|
|
|
that are both a) simple to use, and b) flexible / customizable.
|
|
|
|
|
By allowing defaults, we can have our cake and eat it too,
|
|
|
|
|
enabling both a) and b) concurrently.
|
|
|
|
|
|
|
|
|
|
4. Type defaults also aid in API evolution.
|
|
|
|
|
Consider a situation such as `Arbitrary` from above;
|
|
|
|
|
The API might have originally been:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Arbitrary: Sized + fmt::Debug {
|
|
|
|
|
fn arbitrary() -> Self::Strategy;
|
2019-06-10 04:00:24 +08:00
|
|
|
|
|
2018-08-24 01:08:55 +08:00
|
|
|
|
type Strategy: Strategy<Value = Self>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-27 12:19:23 +08:00
|
|
|
|
with an implementation:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
impl Arbitrary for usize {
|
|
|
|
|
fn arbitrary() -> Self::Strategy { 0..100 }
|
|
|
|
|
|
|
|
|
|
type Strategy = Range<usize>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-24 01:08:55 +08:00
|
|
|
|
By allowing defaults, we can transition to this more flexible API without
|
|
|
|
|
breaking any consumers by simply saying:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Arbitrary: Sized + fmt::Debug {
|
|
|
|
|
type Parameters: Default = ();
|
2019-06-10 04:00:24 +08:00
|
|
|
|
|
2018-08-24 01:08:55 +08:00
|
|
|
|
fn arbitrary() -> Self::Strategy {
|
|
|
|
|
Self::arbitrary_with(Default::default())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
|
|
|
|
Self::arbitrary()
|
|
|
|
|
// This co-recursive definition will blow the stack.
|
|
|
|
|
// However; since we can assume that previous implementors
|
|
|
|
|
// actually provided a definition for `arbitrary` that
|
|
|
|
|
// can't possibly reference `arbitrary_with`, we are OK.
|
|
|
|
|
// You would only run into trouble for new implementations;
|
|
|
|
|
// but that can be dealt with in documentation.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Strategy: Strategy<Value = Self>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-27 12:19:23 +08:00
|
|
|
|
The implementation `Arbitrary for usize` *remains valid* even after the change.
|
|
|
|
|
|
2018-08-24 01:08:55 +08:00
|
|
|
|
# Guide-level explanation
|
|
|
|
|
[guide-level-explanation]: #guide-level-explanation
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Background and The status quo
|
2018-08-24 01:08:55 +08:00
|
|
|
|
[background]: #background-and-the-status-quo
|
|
|
|
|
|
|
|
|
|
Let's consider a simple trait with an associated type and another item (1):
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
|
|
|
|
type Bar;
|
|
|
|
|
|
|
|
|
|
const QUUX: Self::Bar;
|
|
|
|
|
|
|
|
|
|
fn wibble(x: Self::Bar) -> u8;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Ever since [RFC 192],
|
|
|
|
|
Rust has been capable of assigning default types to associated types as in (2):
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
#![feature(associated_type_defaults)]
|
|
|
|
|
|
|
|
|
|
trait Foo {
|
|
|
|
|
type Bar = u8;
|
|
|
|
|
|
|
|
|
|
const QUUX: Self::Bar = 42u8;
|
|
|
|
|
|
|
|
|
|
fn wibble(x: Self::Bar) -> u8 { x }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
However, unlike as specified in [RFC 192], which would permit (2),
|
|
|
|
|
the current implementation rejects (2) with the following error messages (3):
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
error[E0308]: mismatched types
|
|
|
|
|
--> src/lib.rs:6:29
|
|
|
|
|
|
|
|
|
|
|
6 | const QUUX: Self::Bar = 42u8;
|
|
|
|
|
| ^^^^ expected associated type, found u8
|
|
|
|
|
|
|
|
|
|
|
= note: expected type `<Self as Foo>::Bar`
|
|
|
|
|
found type `u8`
|
|
|
|
|
|
|
|
|
|
error[E0308]: mismatched types
|
|
|
|
|
--> src/lib.rs:8:37
|
|
|
|
|
|
|
|
|
|
|
8 | fn wibble(x: Self::Bar) -> u8 { x }
|
|
|
|
|
| -- ^ expected u8, found associated type
|
|
|
|
|
| |
|
|
|
|
|
| expected `u8` because of return type
|
|
|
|
|
|
|
|
|
|
|
= note: expected type `u8`
|
|
|
|
|
found type `<Self as Foo>::Bar`
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The compiler rejects snippet (2) to preserve the soundness of the type system.
|
|
|
|
|
It must be rejected because a user might write (4):
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Bar { ... }
|
|
|
|
|
|
|
|
|
|
impl Foo for Bar {
|
|
|
|
|
type Bar = Vec<u8>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Given snippet (4), `Self::Bar` will evaluate to `Vec<u8>`,
|
|
|
|
|
which is therefore the type of `<Bar as Foo>::QUUX`.
|
|
|
|
|
However, we have not given a different value for the constant,
|
|
|
|
|
and so it must be `42u8`, which has the type `u8`.
|
|
|
|
|
Therefore, we have reached an inconsistency in the type system:
|
|
|
|
|
`<Bar as Foo>::QUUX` is of value `42u8`, but of type `Vec<u8>`.
|
|
|
|
|
So we may accept either `impl Foo for Bar` as defined in (4),
|
|
|
|
|
or the definition of `Foo` as in (2), but not *both*.
|
|
|
|
|
|
|
|
|
|
[RFC 192] solved this dilemma by rejecting the implementation
|
|
|
|
|
and insisting that if you override *one* associated type,
|
|
|
|
|
then you must override *all* other defaulted items.
|
|
|
|
|
Or stated in its own words:
|
|
|
|
|
|
|
|
|
|
> + If a trait implementor overrides any default associated types,
|
|
|
|
|
> they must also override all default functions and methods.
|
|
|
|
|
> + Otherwise, a trait implementor can selectively override individual
|
|
|
|
|
> default methods/functions, as they can today.
|
|
|
|
|
|
|
|
|
|
Meanwhile, as we saw in the error message above (3),
|
|
|
|
|
the current implementation takes the alternative approach of accepting
|
|
|
|
|
`impl Foo for Bar` (4) but not the definition of `Foo` as in (2).
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Changes in this RFC
|
2018-08-24 01:08:55 +08:00
|
|
|
|
[changes]: #changes-in-this-rfc
|
|
|
|
|
|
|
|
|
|
In this RFC, we change the approach in [RFC 192] to the currently implemented
|
|
|
|
|
approach. Thus, you will continue to receive the error message above
|
|
|
|
|
and you will be able to provide associated type defaults.
|
|
|
|
|
|
|
|
|
|
[specialization]: https://github.com/rust-lang/rfcs/pull/1210
|
|
|
|
|
|
|
|
|
|
With respect to [specialization], the behaviour is the same.
|
|
|
|
|
That is, if you write (5):
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
#![feature(specialization)]
|
|
|
|
|
|
|
|
|
|
trait Foo {
|
|
|
|
|
type Bar;
|
|
|
|
|
|
|
|
|
|
fn quux(x: Self::Bar) -> u8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Wibble<T>;
|
|
|
|
|
|
|
|
|
|
impl<T> Foo for Wibble<T> {
|
|
|
|
|
default type Bar = u8;
|
|
|
|
|
|
|
|
|
|
default fn quux(x: Self::Bar) -> u8 { x }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The compiler will reject this because you are not allowed to assume,
|
|
|
|
|
just like before, that `x: u8`. The reason why is much the same as
|
|
|
|
|
we have previously discussed in the [background].
|
|
|
|
|
|
2018-08-27 12:23:05 +08:00
|
|
|
|
[current_impl_diverge]: https://play.rust-lang.org/?gist=30e01d77f7045359e30c7d3f3144e984&version=nightly&mode=debug&edition=2015
|
|
|
|
|
|
2018-08-27 06:05:19 +08:00
|
|
|
|
One place where this proposal diverges from what is currently implemented
|
2018-08-27 12:23:05 +08:00
|
|
|
|
is with respect to the [following example][current_impl_diverge] (6):
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
#![feature(associated_type_defaults)]
|
|
|
|
|
|
|
|
|
|
trait Foo {
|
|
|
|
|
type Bar = usize;
|
|
|
|
|
|
|
|
|
|
fn baz(x: Self::Bar) -> usize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> Foo for Vec<T> {
|
|
|
|
|
fn baz(x: Self::Bar) -> usize { x }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In the current implementation, (6) is rejected because the compiler will not
|
|
|
|
|
let you assume that `x` is of type `usize`. But in this proposal, you would be
|
2018-08-27 06:05:19 +08:00
|
|
|
|
allowed to assume this. To permit this is not a problem because `Foo for Vec<T>`
|
2019-06-10 04:06:01 +08:00
|
|
|
|
is not further specializable since `Bar` in the implementation has not been
|
2018-08-26 11:16:09 +08:00
|
|
|
|
marked as `default`.
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Trait objects
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Another divergence in this RFC as compared to the current implementation is
|
|
|
|
|
with respect to trait objects. Currently, if you write (7):
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
|
|
|
|
type Bar = u8;
|
|
|
|
|
fn method(&self) -> Self::Bar;
|
|
|
|
|
}
|
2018-08-27 06:05:19 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
type Alpha = Box<dyn Foo>;
|
|
|
|
|
```
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
the compiler will reject it with (8):
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
2019-01-18 20:10:13 +08:00
|
|
|
|
error[E0191]: the value of the associated type `Bar` (from the trait `Foo`) must be specified
|
|
|
|
|
--> src/lib.rs:8:17
|
|
|
|
|
|
|
|
|
|
|
4 | type Bar = u8;
|
|
|
|
|
| -------------- `Bar` defined here
|
|
|
|
|
...
|
|
|
|
|
8 | type Alpha = Box<dyn Foo>;
|
|
|
|
|
| ^^^^^^^ associated type `Bar` must be specified
|
|
|
|
|
```
|
2018-08-27 06:58:58 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
With this RFC however, the error in (8) will disappear and (7) will be *accepted*.
|
|
|
|
|
That is, `Box<dyn Foo>` is taken as equivalent as `Box<dyn Foo<Bar = u8>>`.
|
2018-08-27 06:58:58 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
If we complicate the situation slightly and introduce another associated `type`
|
|
|
|
|
`Baz` which refers to `Bar` in its default, the compiler will still let us
|
|
|
|
|
elide specifying the defaults (9):
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
|
|
|
|
type Bar = u8;
|
|
|
|
|
type Baz = Vec<Self::Bar>;
|
|
|
|
|
|
|
|
|
|
fn method(&self) -> (Self::Bar, Self::Baz);
|
2018-08-24 01:08:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
type Alpha = Box<dyn Foo>;
|
|
|
|
|
// -------
|
|
|
|
|
// Same as: `dyn Foo<Bar = u8, Baz = Vec<u8>>`.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
type Beta = Box<dyn Foo<Bar = u16>>;
|
|
|
|
|
// ------------------
|
|
|
|
|
// Same as: `dyn Foo<Bar = u16, Baz = Vec<u16>>`.
|
|
|
|
|
```
|
2018-08-27 06:58:58 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Note that in `Beta`, `Bar` was specified but `Baz` was not.
|
|
|
|
|
The compiler can infer that `Baz` is `Vec<u16>` since `Self::Bar = u16` and
|
|
|
|
|
`Baz = Vec<Self::Bar>`.
|
2018-08-27 06:58:58 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
With these changes,
|
|
|
|
|
we consider the design of associated type defaults to be *finalized*.
|
2018-08-27 06:58:58 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
# Reference-level explanation
|
|
|
|
|
[reference-level-explanation]: #reference-level-explanation
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The proposal makes no changes to the dynamic semantics and the grammar of Rust.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Static semantics
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
This section supersedes [RFC 192] with respect to associated type defaults.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Associated types can be assigned a default type in a `trait` definition:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
|
|
|
|
type Bar = $default_type;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
$other_items
|
2018-08-24 01:08:55 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Any item in `$other_items`, which have any provided definitions,
|
|
|
|
|
may only assume that the type of `Self::Bar` is `Self::Bar`.
|
|
|
|
|
They may *not* assume that the underlying type of `Self::Bar` is `$default_type`.
|
|
|
|
|
This property is essential for the soundness of the type system.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
When an associated type default exists in a `trait` definition,
|
|
|
|
|
it need not be specified in the implementations of that `trait`.
|
|
|
|
|
If implementations of that `trait` do not make that associated type
|
|
|
|
|
available for specialization, the `$default_type` may be assumed
|
|
|
|
|
in other items specified in the implementation.
|
|
|
|
|
If an implementation does make the associated type available for
|
|
|
|
|
further specialization, then other definitions in the implementation
|
|
|
|
|
may not assume the given underlying specified type of the associated type
|
2019-06-10 04:42:41 +08:00
|
|
|
|
and may only assume that it is `Self::TheAssociatedType`.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
This applies generally to any item inside a `trait`.
|
|
|
|
|
You may only assume the signature of an item, but not any provided definition,
|
|
|
|
|
in provided definitions of other items.
|
|
|
|
|
For example, this means that you may not assume the value of an
|
|
|
|
|
associated `const` item in other items with provided definition
|
|
|
|
|
in a `trait` definition.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Interaction with `dyn Trait<...>`
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
+ Let `σ` denote a well-formed type.
|
|
|
|
|
+ Let `L` denote a well-formed lifetime.
|
|
|
|
|
+ Let `X` refer to an object safe `trait`.
|
|
|
|
|
+ Let `k` denote the number of lifetime parameters in `X`.
|
|
|
|
|
+ Let `l` denote the number of type parameters in `X`.
|
|
|
|
|
+ Let `m` where `0 ≤ m ≤ l` denote the number of type parameters
|
|
|
|
|
in `X` without specified defaults.
|
|
|
|
|
+ Let `A` denote the set of associated types in `X`.
|
|
|
|
|
+ Let `o = |A|`.
|
|
|
|
|
+ Let `D` where `D ⊆ A` denote set of associated types in `X` with defaults.
|
|
|
|
|
+ Let `E = A \ D`.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Then, in a type of form (where `m ≤ n ≤ l`):
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
dyn X<
|
|
|
|
|
L0, .., Lk,
|
|
|
|
|
σ0, .. σn,
|
|
|
|
|
A0 = σ_{n + 1}, .., Ao = σ_{n + o}
|
|
|
|
|
>
|
|
|
|
|
```
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
the associated types in `E` must be bound in `A0, .., Ao`
|
|
|
|
|
whereas those in `D` may be omitted selectively (i.e. omit zero, some, or all).
|
|
|
|
|
|
|
|
|
|
When inferring the types of the omitted projections in `D`,
|
|
|
|
|
projections in the assigned defaults of types in `D` will use the types in
|
|
|
|
|
`A0, .., Ao` instead of the defaults specified in `D`. For example, if given:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait X {
|
|
|
|
|
type A0 = u8;
|
|
|
|
|
type A1 = Vec<Self::A0>;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
}
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
then the type `dyn X<A0 = u16>` is inferred to `dyn X<A0 = u16, A1 = Vec<u16>>`
|
|
|
|
|
as opposed to `dyn X<A0 = u16, A1 = Vec<u8>>`.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Interaction with `existential type`
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[RFC 2071]: https://github.com/rust-lang/rfcs/blob/master/text/2071-impl-trait-existential-types.md#reference-existential-types
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[RFC 2071] defines a construct `existential type Foo: Bar;` which is permitted
|
|
|
|
|
in associated types and results in an opaque type. This means that the nominal
|
|
|
|
|
type identity is hidden from certain contexts and only `Bar` is extensionally
|
|
|
|
|
known about the type wherefore only the operations of `Bar` is afforded.
|
|
|
|
|
This construct is sometimes written as `type Foo = impl Bar;` in conversation
|
|
|
|
|
instead.
|
|
|
|
|
|
|
|
|
|
[RFC 1210]: https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md#default-impls
|
|
|
|
|
|
|
|
|
|
With respect to this RFC, the semantics of `type Assoc = impl Bar;`
|
|
|
|
|
inside a trait definition, where `Assoc` is the name of the associated type,
|
|
|
|
|
is understood as what it means in terms of `default impl ..` as discussed
|
2019-02-17 06:10:51 +08:00
|
|
|
|
in [RFC 1210]. What this means in concrete terms is that given:
|
2018-08-28 00:43:57 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
2019-01-18 20:10:13 +08:00
|
|
|
|
type Assoc = impl Bar;
|
2018-08-28 00:43:57 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
...
|
2018-08-28 00:43:57 +08:00
|
|
|
|
}
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```
|
2018-08-28 00:43:57 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
the underlying type of `Assoc` stays the same for all implementations which
|
|
|
|
|
do not change the default of `Assoc`. The same applies to specializations.
|
|
|
|
|
With respect to type opacity, it is the same as that of `existential type`.
|
2018-08-28 00:43:57 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
# Drawbacks
|
|
|
|
|
[drawbacks]: #drawbacks
|
2018-08-28 00:43:57 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The main drawbacks of this proposal are that:
|
2018-08-28 00:43:57 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
1. if you have implementations where you commonly would have needed to
|
|
|
|
|
write `default { .. }` because you need to assume the type of an
|
|
|
|
|
associated type default in a provided method, then the solution proposed
|
|
|
|
|
in this RFC is less ergonomic.
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, it is the contention of this RFC that such needs will be less common
|
|
|
|
|
and that the nesting mechanism or other similar ideas will be sufficiently
|
|
|
|
|
ergonomic for such cases. This is discussed below.
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
# Rationale and alternatives
|
|
|
|
|
[rationale-and-alternatives]: #rationale-and-alternatives
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Alternatives
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The main alternative is to retain the behaviour in [RFC 192] such that
|
|
|
|
|
you may assume the type of associated type defaults in provided methods.
|
|
|
|
|
As noted in the [drawbacks] section,
|
|
|
|
|
this would be useful for certain types of APIs.
|
|
|
|
|
However, it is more likely than not that associated type defaults will
|
|
|
|
|
be used as a mechanism for code reuse than for other constructs.
|
|
|
|
|
As such, we consider the approach in this RFC to be more ergonomic.
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Another alternative to the mechanism proposed in this RFC is to somehow
|
|
|
|
|
track which methods rely on which associated types as well as constants.
|
|
|
|
|
However, we have historically had a strong bias toward being explicit
|
|
|
|
|
in signatures about such things, avoiding to infer them.
|
|
|
|
|
With respect to semantic versioning, such an approach may also cause
|
|
|
|
|
surprises for crate authors and their dependents alike because it may
|
|
|
|
|
be difficult at glance to decide what the dependencies are.
|
|
|
|
|
This in turn reduces the maintainability and readability of code.
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Consistency with associated `const`s
|
|
|
|
|
|
|
|
|
|
Consider the following valid example from stable Rust:
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
2019-01-18 20:10:13 +08:00
|
|
|
|
trait Foo {
|
|
|
|
|
const BAR: usize = 1;
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
fn baz() { println!("Hi I'm baz."); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Foo for () {
|
|
|
|
|
fn baz() { println!("Hi I'm () baz."); }
|
2018-08-27 09:40:37 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
As we can see, you are permitted to override `baz` but leave `BAR` defaulted.
|
|
|
|
|
This is consistent with the behaviour in this RFC in that it has the same
|
|
|
|
|
property: *"you don't need to override all items if you override one"*.
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Consistency and uniformity of any programming language is vital to make
|
|
|
|
|
its learning easy and to rid users of surprising corner cases and caveats.
|
|
|
|
|
By staying consistent, as shown above, we can reduce the cost to our complexity
|
|
|
|
|
budget that associated type defaults incur.
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Overriding everything is less ergonomic
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
We have already discussed this to some extent.
|
|
|
|
|
Another point to consider is that Rust code frequently sports traits such as
|
|
|
|
|
`Iterator` and `Future` that have many provided methods and few associated types.
|
|
|
|
|
While these particular traits may not benefit from associated type defaults,
|
|
|
|
|
many other traits, such as `Arbitrary` defined in the [motivation], would.
|
2018-09-19 06:13:18 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## True API evolution by inferring in `dyn Trait`
|
2018-09-19 06:13:18 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
While `impl Trait` will not take associated type defaults into account,
|
|
|
|
|
`dyn trait` will. This may seem inconsistent. However, it is justified by the
|
|
|
|
|
inherent difference in semantics between these constructs and by the goal set
|
|
|
|
|
out in the [motivation] to facilitate API evolution.
|
|
|
|
|
|
|
|
|
|
As an illustration, consider `Iterator`:
|
2018-09-19 06:13:18 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
2019-01-18 20:10:13 +08:00
|
|
|
|
trait Iterator {
|
|
|
|
|
type Item;
|
2018-09-19 06:13:18 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
...
|
2018-09-19 06:13:18 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Currently, you may write:
|
2018-09-19 06:13:18 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
2019-01-18 20:10:13 +08:00
|
|
|
|
fn foo() -> impl Iterator { 0..1 }
|
2018-09-19 06:13:18 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
and when `foo` is called, you will know nothing about `Item`.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, you cannot write:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
2019-01-18 20:10:13 +08:00
|
|
|
|
fn bar() -> Box<dyn Iterator> { Box::new(0..1) }
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
since the associated type `Item` is not specified.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
In `bar`, the type of `Item` is unknown and so the compiler does not know how
|
|
|
|
|
to generate the vtable. As a result, an error is emitted:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
L | fn bar() -> Box<dyn Iterator> { Box::new(0..1) }
|
|
|
|
|
| ^^^^^^^^^^^^ missing associated type `Item` value
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
If we introduced a default for `Item`:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
type Item = ();
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
then `bar` would become legal under this RFC and so strictly more code than
|
|
|
|
|
today would be accepted.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Meanwhile, if `impl Iterator` meant `impl Iterator<Item = ()>`,
|
|
|
|
|
this would impose a stronger requirement on existing code where `impl Iterator`
|
|
|
|
|
is used and thus it would be a breaking change to the users of `Iterator`.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
For `Iterator`, it would not be helpful to introduce a default for `Item`.
|
|
|
|
|
However, for the purposes of API evolution, the value is not in assigning
|
|
|
|
|
defaults to the existing associated types of a trait. Rather, the value comes
|
|
|
|
|
from being able to add associated types without breaking dependent crates.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Due to the possible breakage of `dyn Trait<..>` when adding an associated type
|
|
|
|
|
to `Trait`, to truly achieve API evolution, defaults must be taken into account
|
|
|
|
|
and be inferable for `dyn Trait`. The opposite is true for `impl Trait`.
|
|
|
|
|
To facilitate API evolution, stronger requirements must not be placed on
|
|
|
|
|
`impl Trait` and therefore defaults should not be taken into account.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
# Prior art
|
|
|
|
|
[prior-art]: #prior-art
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Haskell
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[associated type defaults]: https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/at-syns.pdf
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
As Rust traits are a form of type classes,
|
|
|
|
|
we naturally look for prior art from were they first were introduced.
|
|
|
|
|
That language, being Haskell,
|
|
|
|
|
permits a user to specify [associated type defaults].
|
|
|
|
|
For example, we may write the following legal program:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```haskell
|
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
class Foo x where
|
|
|
|
|
type Bar x :: *
|
|
|
|
|
-- A default:
|
|
|
|
|
type Bar x = Int
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
-- Provided method:
|
|
|
|
|
baz :: x -> Bar x -> Int
|
|
|
|
|
baz _ _ = 0
|
|
|
|
|
|
|
|
|
|
data Quux = Quux
|
|
|
|
|
|
|
|
|
|
instance Foo Quux where
|
|
|
|
|
baz _ y = y
|
2018-09-19 11:36:53 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
As in this proposal, we may assume that `y :: Int` in the above snippet.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
In this case, we are not assuming that `Bar x` unifies with `Int` in the `class`.
|
|
|
|
|
Let's try to assume that now:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```haskell
|
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
class Foo x where
|
|
|
|
|
type Bar x :: *
|
|
|
|
|
-- A default:
|
|
|
|
|
type Bar x = Int
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
-- Provided method:
|
|
|
|
|
baz :: x -> Bar x -> Int
|
|
|
|
|
baz _ barX = barX
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
This snippet results in a type checking error (tested on GHC 8.0.1):
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
|
|
|
|
```
|
2019-01-18 20:10:13 +08:00
|
|
|
|
main.hs:11:16: error:
|
|
|
|
|
• Couldn't match expected type ‘Int’ with actual type ‘Bar x’
|
|
|
|
|
• In the expression: barX
|
|
|
|
|
In an equation for ‘baz’: baz _ barX = barX
|
|
|
|
|
• Relevant bindings include
|
|
|
|
|
barX :: Bar x (bound at main.hs:11:9)
|
|
|
|
|
baz :: x -> Bar x -> Int (bound at main.hs:11:3)
|
|
|
|
|
<interactive>:3:1: error:
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The thing to pay attention to here is:
|
|
|
|
|
> Couldn't match expected type ‘`Int`’ with actual type ‘`Bar x`’
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
We can clearly see that the type checker is *not* allowing us to assume
|
|
|
|
|
that `Int` and `Bar x` are the same type.
|
|
|
|
|
This is consistent with the approach this RFC proposes.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
To our knowledge, Haskell does not have any means such as `default { .. }`
|
|
|
|
|
to change this behaviour. Presumably, this is the case because Haskell
|
2019-02-20 20:33:43 +08:00
|
|
|
|
preserves parametricity thus lacking specialization, wherefore `default { .. }`,
|
|
|
|
|
as suggested in the [future possibilities][future-possibilities],
|
2019-01-18 20:10:13 +08:00
|
|
|
|
might not carry its weight.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Idris
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[idris_interface]: http://docs.idris-lang.org/en/latest/tutorial/interfaces.html
|
|
|
|
|
[coherence]: http://blog.ezyang.com/2014/07/type-classes-confluence-coherence-global-uniqueness/
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Idris has a concept it calls [`interface`s][idris_interface].
|
|
|
|
|
These resemble type classes in Haskell, and by extension traits in Rust.
|
|
|
|
|
However, unlike Haskell and Rust, these `interface`s do not have the property
|
|
|
|
|
of [coherence] and will permit multiple implementations of the same interface.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Since Idris is language with full spectrum dependent types,
|
|
|
|
|
it does not distinguish between terms and types, instead, types are terms.
|
|
|
|
|
Therefore, there is really not a distinct concept called "associated type".
|
|
|
|
|
However, an `interface` may require certain definitions to be provided
|
|
|
|
|
and this includes types. For example, we may write:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```idris
|
|
|
|
|
interface Iterator self where
|
|
|
|
|
item : Type
|
|
|
|
|
next : self -> Maybe (self, item)
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
implementation Iterator (List a) where
|
|
|
|
|
item = a
|
|
|
|
|
next [] = Nothing
|
|
|
|
|
next (x :: xs) = Just (xs, x)
|
2018-09-19 11:36:53 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Like in Haskell, in Idris, a function or value in an interface may be given a
|
|
|
|
|
default definition. For example, the following is a valid program:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```idris
|
|
|
|
|
interface Foo x where
|
|
|
|
|
bar : Type
|
|
|
|
|
bar = Bool
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
baz : x -> bar
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
implementation Foo Int where
|
|
|
|
|
baz x = x == 0
|
2018-08-24 01:08:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, if we provide a default for `baz` in the `interface` which assumes
|
|
|
|
|
the default value `Bool` of `bar`, as with the following example:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```idris
|
|
|
|
|
interface Foo x where
|
|
|
|
|
bar : Type
|
|
|
|
|
bar = Bool
|
|
|
|
|
|
|
|
|
|
baz : x -> bar
|
|
|
|
|
baz _ = True
|
2018-09-19 11:36:53 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
then we run into an error:
|
|
|
|
|
|
2018-09-19 11:36:53 +08:00
|
|
|
|
```
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Type checking .\foo.idr
|
|
|
|
|
foo.idr:6:13-16:
|
|
|
|
|
|
|
|
|
|
|
6 | baz _ = True
|
|
|
|
|
| ~~~~
|
|
|
|
|
When checking right hand side of Main.default#baz with expected type
|
|
|
|
|
bar x _
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Type mismatch between
|
|
|
|
|
Bool (Type of True)
|
|
|
|
|
and
|
|
|
|
|
bar x _ (Expected type)
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The behaviour here is exactly as in Haskell and as proposed in this RFC.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## C++
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
In C++, it is possible to provide associated types and specialize them as well.
|
|
|
|
|
This is shown in the following example:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```cpp
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <string>
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
template<typename T> struct wrap {};
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
template<typename T> struct foo { // Unspecialized.
|
|
|
|
|
using bar = int;
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
bar make_a_bar() { return 0; };
|
|
|
|
|
};
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
template<typename T> struct foo<wrap<T>> { // Partial specialization.
|
|
|
|
|
using bar = std::string;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
bar make_a_bar() { return std::string("hello world"); };
|
|
|
|
|
};
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
int main() {
|
|
|
|
|
foo<void> a_foo;
|
|
|
|
|
std::cout << a_foo.make_a_bar() << std::endl;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
foo<wrap<void>> b_foo;
|
|
|
|
|
std::cout << b_foo.make_a_bar() << std::endl;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
You will note that C++ allows us to assume in both the base template class,
|
|
|
|
|
as well as the specialization, that `bar` is equal to the underlying type.
|
|
|
|
|
This is because one cannot specialize any part of a class without specializing
|
|
|
|
|
the whole of it. It's equivalent to one atomic `default { .. }` block.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Swift
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[swift_assoc]: https://docs.swift.org/swift-book/LanguageGuide/Generics.html
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
One language which does have [associated types][swift_assoc] and defaults but
|
|
|
|
|
which does not have provided definitions for methods is Swift.
|
|
|
|
|
As an example, we may write:
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```swift
|
|
|
|
|
protocol Foo {
|
|
|
|
|
associatedtype Bar = Int
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
func append() -> Bar
|
|
|
|
|
}
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
struct Quux: Foo {
|
|
|
|
|
func baz() -> Bar {
|
|
|
|
|
return 1
|
|
|
|
|
}
|
2018-08-27 09:40:37 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, we may not write:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```swift
|
|
|
|
|
protocol Foo {
|
|
|
|
|
associatedtype Bar = Int
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
func append() -> Bar { return 0 }
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
This would result in:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```
|
|
|
|
|
main.swift:4:23: error: protocol methods may not have bodies
|
|
|
|
|
func baz() -> Bar { return 0 }
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Scala
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Another language which allows for these kinds of type projections and defaults
|
|
|
|
|
for them is Scala. While Scala does not have type classes like Rust and Haskell
|
|
|
|
|
does, it does have a concept of `trait` which can be likened to a sort of
|
|
|
|
|
incoherent "type class" system. For example, we may write:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```scala
|
2018-08-24 01:08:55 +08:00
|
|
|
|
trait Foo {
|
2019-01-18 20:10:13 +08:00
|
|
|
|
type Bar = Int
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
def baz(x: Bar): Int = x
|
2018-08-24 01:08:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
class Quux extends Foo {
|
|
|
|
|
override type Bar = Int
|
|
|
|
|
override def baz(x: Bar): Int = x
|
2018-08-24 01:08:55 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
There are a few interesting things to note here:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
1. We are allowed to specify a default type `Int` for `Bar`.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
2. A default definition for `baz` may be provided.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
3. This default definition may assume the default given for `Bar`.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
4. However, we *must* explicitly state that we are overriding `baz`.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
5. If we change the definition of of `override type Bar` to `Double`,
|
|
|
|
|
the Scala compiler will reject it.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
# Unresolved questions
|
|
|
|
|
[unresolved-questions]: #unresolved-questions
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-02-01 05:41:33 +08:00
|
|
|
|
## 1. When do suitability of defaults need to be proven?
|
|
|
|
|
|
|
|
|
|
Consider a trait `Foo<T>` defined as:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Foo<T> {
|
|
|
|
|
type Bar: Clone = Vec<T>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Let's also assume the following implementation of `Clone`:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
impl<T: Clone> Clone for Vec<T> { ... }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To prove that `Vec<T>: Clone`, we must prove that `T: Clone`.
|
|
|
|
|
However, `Foo<T>` does not say that `T: Clone` so is its definition valid?
|
|
|
|
|
If the suitability of `Vec<T>` is checked where `Foo<T>` is defined (1),
|
|
|
|
|
then we don't know that `T: Clone` and so the definition must be rejected.
|
|
|
|
|
To make the compiler admit `Foo<T>`, we would have to write:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Foo<T: Clone> {
|
|
|
|
|
type Bar: Clone = Vec<T>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Now it is provable that `T: Clone` so `Vec<T>: Clone` which is what was required.
|
|
|
|
|
|
|
|
|
|
If instead the suitability of defaults are checked in `impl`ementations (2),
|
|
|
|
|
then proving `Vec<T>: Clone` would not be required in `Foo<T>`'s definition and
|
|
|
|
|
so then `Foo<T>` would type-check. As a result, it would be admissible to write:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
|
struct A;
|
|
|
|
|
|
|
|
|
|
struct B;
|
|
|
|
|
|
|
|
|
|
impl Foo<A> for B {}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
since `Vec<A>: Clone` holds.
|
|
|
|
|
|
|
|
|
|
With condition (2), strictly more programs are accepted than with (1).
|
|
|
|
|
It may be that useful programs are rejected if we enforce (1) rather than (2).
|
|
|
|
|
However, it would also be the more conservative choice, allowing us to move
|
|
|
|
|
towards (2) when necessary. As it is currently unclear what solution is best,
|
|
|
|
|
this question is left unresolved.
|
|
|
|
|
|
2019-02-25 21:15:42 +08:00
|
|
|
|
## 2. Where are cycles checked?
|
2019-02-01 05:41:33 +08:00
|
|
|
|
|
|
|
|
|
[playground]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e823eea5e7ecba5da78cff225e0adaf9
|
|
|
|
|
|
|
|
|
|
Consider a program *([playground])*:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
#![feature(associated_type_defaults)]
|
|
|
|
|
|
|
|
|
|
trait A {
|
|
|
|
|
type B = Self::C; // B defaults to C,
|
|
|
|
|
type C = Self::B; // C defaults to B, and we have a cycle!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl A for () {}
|
|
|
|
|
|
|
|
|
|
fn _foo() {
|
|
|
|
|
let _x: <() as A>::B;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Removing this function will make the example compile.
|
|
|
|
|
fn main() {
|
|
|
|
|
let _x: <() as A>::B;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Currently, this results in a crash. This will need to be fixed.
|
|
|
|
|
At the very latest, `impl A for () {}` should have been an error.
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait A {
|
|
|
|
|
type B = Self::C;
|
|
|
|
|
type C = Self::B;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl A for () {} // This OK but shouldn't be.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If cycles are checked for in `impl A for ()`, then it would be valid to write:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait A {
|
|
|
|
|
type B = Self::C;
|
|
|
|
|
type C = Self::B;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl A for () {
|
|
|
|
|
type B = u8; // The cycle is broken!
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Alternatively, cycles could be checked for in `A`'s definition.
|
|
|
|
|
This is similar to the previous question in (1).
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
# Future possibilities
|
|
|
|
|
[future-possibilities]: #future-possibilities
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
This section in the RFC used to be part of the proposal. To provide context
|
|
|
|
|
for considerations made in the proposal, it is recorded here.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Summary
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[Introduce][default_groups] the concept of `default { .. }` groups in traits
|
|
|
|
|
and their implementations which may be used to introduce atomic units of
|
|
|
|
|
specialization (if anything in the group is specialized, everything must be).
|
|
|
|
|
These groups may be nested and form a [tree of cliques].
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Motivation
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### For `default { .. }` groups
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Finally, because we are making [changes] to how associated type defaults work
|
|
|
|
|
in this RFC, a new mechanism is required to regain the loss of expressive power
|
|
|
|
|
due to these changes. This mechanism is described in the section on
|
|
|
|
|
[`default { .. }` groups][default_groups] as alluded to in the summary.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
These groups not only retain the expressive power due to [RFC 192] but extend
|
|
|
|
|
power such that users get fine grained control over what things may and may not
|
|
|
|
|
be overridden together. In addition, these groups allow users to assume the
|
|
|
|
|
definition of type defaults in other items in a way that preserves soundness.
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Examples where it is useful for other items to assume the default of an
|
|
|
|
|
associated type include:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[issue#29661]: https://github.com/rust-lang/rust/issues/29661
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[comment174527854]: https://github.com/rust-lang/rust/issues/29661#issuecomment-174527854
|
|
|
|
|
[comment280944035]:https://github.com/rust-lang/rust/issues/29661#issuecomment-280944035
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
1. [A default method][comment174527854] whose
|
|
|
|
|
[return type is an associated type:][comment280944035]
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
/// "Callbacks" for a push-based parser
|
|
|
|
|
trait Sink {
|
|
|
|
|
fn handle_foo(&mut self, ...);
|
2019-06-10 04:00:24 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
default {
|
|
|
|
|
type Output = Self;
|
2019-06-10 04:00:24 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
// OK to assume what `Output` really is because any overriding
|
2022-10-08 13:46:46 +08:00
|
|
|
|
// must override both `Output` and `finish`.
|
2019-01-18 20:10:13 +08:00
|
|
|
|
fn finish(self) -> Self::Output { self }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
2. There are plenty of other examples in [rust-lang/rust#29661][issue#29661].
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[issue#31844]: https://github.com/rust-lang/rust/issues/31844
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
3. Other examples where `default { .. }` would have been useful can be found
|
|
|
|
|
in the [tracking issue][issue#31844] for [specialization]:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
+ <https://github.com/rust-lang/rust/issues/31844#issuecomment-198853202>
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
You can see `default { .. }` being used
|
|
|
|
|
[here](https://github.com/rust-lang/rust/issues/31844#issuecomment-249355377).
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
+ <https://github.com/rust-lang/rust/issues/31844#issuecomment-230093545>
|
|
|
|
|
+ <https://github.com/rust-lang/rust/issues/31844#issuecomment-247867693>
|
|
|
|
|
+ <https://github.com/rust-lang/rust/issues/31844#issuecomment-263175793>
|
|
|
|
|
+ <https://github.com/rust-lang/rust/issues/31844#issuecomment-279350986>
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
[`std::remove_reference`]: http://www.cplusplus.com/reference/type_traits/remove_reference/
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
4. Encoding a more powerful [`std::remove_reference`]
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
We can encode a more powerful version of C++'s `remove_reference` construct,
|
|
|
|
|
which allows you to get the base type of a reference type recursively.
|
|
|
|
|
Without default groups, we can get access to the base type like so:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
trait RemoveRef {
|
|
|
|
|
type WithoutRef;
|
|
|
|
|
}
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl<T> RemoveRef for T {
|
|
|
|
|
default type WithoutRef = T;
|
|
|
|
|
}
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl<'a, T: RemoveRef> RemoveRef for &'a T {
|
|
|
|
|
type WithoutRef = T::WithoutRef;
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, we don't have any way to transitively dereference to
|
|
|
|
|
`&Self::WithoutRef`. With default groups we can gain that ability with:
|
2018-09-19 11:36:53 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
trait RemoveRef {
|
|
|
|
|
type WithoutRef;
|
|
|
|
|
fn single_ref(&self) -> &Self::WithoutRef;
|
|
|
|
|
}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl<T> RemoveRef for T {
|
|
|
|
|
default {
|
|
|
|
|
type WithoutRef = T;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
fn single_ref(&self) -> &Self::WithoutRef {
|
|
|
|
|
// We can assume that `T == Self::WithoutRef`.
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl<'a, T: RemoveRef> RemoveRef for &'a T {
|
|
|
|
|
type WithoutRef = T::WithoutRef;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
fn single_ref(&self) -> &Self::WithoutRef {
|
|
|
|
|
// We can assume that `T::WithoutRef == Self::WithoutRef`.
|
|
|
|
|
T::single_ref(*self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
We can then proceed to writing things such as:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn do_stuff(recv: impl RemoveRef<WithoutRef: MyTrait>) {
|
|
|
|
|
recv.single_ref().my_method();
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Guide-level explanation
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### `default` specialization groups
|
|
|
|
|
[default_groups]: #default-specialization-groups
|
|
|
|
|
|
|
|
|
|
Note: Overlapping implementations, where one is more specific than the other,
|
|
|
|
|
requires actual support for [specialization].
|
|
|
|
|
|
|
|
|
|
Now, you might be thinking: - *"Well, what if I __do__ need to assume that
|
|
|
|
|
my defaulted associated type is what I said in a provided method,
|
|
|
|
|
what do I do then?"*. Don't worry; We've got you covered.
|
|
|
|
|
|
|
|
|
|
To be able to assume that `Self::Bar` is truly `u8` in snippets (2) and (5),
|
|
|
|
|
you may henceforth use `default { .. }` to group associated items into atomic
|
|
|
|
|
units of specialization. This means that if one item in `default { .. }` is
|
|
|
|
|
overridden in an implementation, then all all the items must be. An example (7):
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Country(&'static str);
|
|
|
|
|
|
|
|
|
|
struct LangSec { papers: usize }
|
|
|
|
|
struct CategoryTheory { papers: usize }
|
|
|
|
|
|
|
|
|
|
trait ComputerScientist {
|
|
|
|
|
default {
|
|
|
|
|
type Details = Country;
|
|
|
|
|
const THE_DETAILS: Self::Details = Country("Scotland"); // OK!
|
|
|
|
|
fn papers(details: Self::Details) -> u8 { 19 } // OK!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/Emily_Riehl
|
|
|
|
|
struct EmilyRiehl;
|
|
|
|
|
|
|
|
|
|
// https://www.cis.upenn.edu/~sweirich/
|
|
|
|
|
struct StephanieWeirich;
|
|
|
|
|
|
|
|
|
|
// http://www.cse.chalmers.se/~andrei/
|
|
|
|
|
struct AndreiSabelfeld;
|
|
|
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/Conor_McBride
|
|
|
|
|
struct ConorMcBride;
|
|
|
|
|
|
|
|
|
|
impl ComputerScientist for EmilyRiehl {
|
|
|
|
|
type Details = CategoryTheory;
|
|
|
|
|
|
|
|
|
|
// ERROR! You must override THE_DETAILS and papers.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ComputerScientist for StephanieWeirich {
|
|
|
|
|
const THE_DETAILS: Country = Country("USA");
|
|
|
|
|
fn papers(details: Self::Details) -> u8 { 86 }
|
|
|
|
|
|
|
|
|
|
// ERROR! You must override Details.
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-10 04:00:24 +08:00
|
|
|
|
impl ComputerScientist for AndreiSabelfeld {
|
2019-01-18 20:10:13 +08:00
|
|
|
|
type Details = LangSec;
|
|
|
|
|
const THE_DETAILS: Self::Details = LangSec { papers: 90 };
|
|
|
|
|
fn papers(details: Self::Details) -> u8 { details.papers }
|
|
|
|
|
|
|
|
|
|
// OK! We have overridden all items in the group.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ComputerScientist for ConorMcBride {
|
|
|
|
|
// OK! We have not overridden anything in the group.
|
|
|
|
|
}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
You may also use `default { .. }` in implementations.
|
|
|
|
|
When you do so, everything in the group is automatically overridable.
|
|
|
|
|
For any items outside the group, you may assume their signatures,
|
|
|
|
|
but not the default definitions given. An example:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
trait Fruit {
|
|
|
|
|
type Details;
|
|
|
|
|
fn foo();
|
|
|
|
|
fn bar();
|
|
|
|
|
fn baz();
|
|
|
|
|
}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:15:12 +08:00
|
|
|
|
struct Citrus<S> { species: S }
|
|
|
|
|
struct Orange<V> { variety: V }
|
2019-01-18 20:10:13 +08:00
|
|
|
|
struct Blood;
|
|
|
|
|
struct Common;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl<S> Fruit for Citrus<S> {
|
|
|
|
|
default {
|
|
|
|
|
type Details = bool;
|
|
|
|
|
fn foo() {
|
|
|
|
|
let _: Self::Details = true; // OK!
|
|
|
|
|
}
|
|
|
|
|
fn bar() {
|
|
|
|
|
let _: Self::Details = true; // OK!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn baz() { // Removing this item here causes an error.
|
|
|
|
|
let _: Self::Details = true;
|
|
|
|
|
// ERROR! You may not assume that `Self::Details == bool` here.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<V> Fruit for Citrus<Orange<V>> {
|
|
|
|
|
default {
|
|
|
|
|
type Details = u8;
|
|
|
|
|
fn foo() {
|
|
|
|
|
let _: Self::Details = 42u8; // OK!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn bar() { // Removing this item here causes an error.
|
|
|
|
|
let _: Self::Details = true;
|
|
|
|
|
// ERROR! You may not assume that `Self::Details == bool` here,
|
|
|
|
|
// even tho we specified that in `Fruit for Citrus<S>`.
|
|
|
|
|
let _: Self::Details = 22u8;
|
|
|
|
|
// ERROR! Can't assume that it's u8 either!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Fruit for Citrus<Orange<Common>> {
|
|
|
|
|
default {
|
|
|
|
|
type Details = f32;
|
|
|
|
|
fn foo() {
|
|
|
|
|
let _: Self::Details = 1.0f32; // OK!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Fruit for Citrus<Orange<Blood>> {
|
|
|
|
|
default {
|
|
|
|
|
type Details = f32;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn foo() {
|
|
|
|
|
let _: Self::Details = 1.0f32;
|
|
|
|
|
// ERROR! Can't assume it is f32.
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
So far our examples have always included an associated type.
|
|
|
|
|
However, this is not a requirement.
|
|
|
|
|
We can also group associated `const`s and `fn`s together or just `fn`s.
|
|
|
|
|
An example:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
|
|
|
|
default {
|
|
|
|
|
const BAR: usize = 3;
|
|
|
|
|
|
|
|
|
|
fn baz() -> [u8; Self::BAR] {
|
|
|
|
|
[1, 2, 3]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trait Quux {
|
|
|
|
|
default {
|
|
|
|
|
fn wibble() {
|
|
|
|
|
...
|
|
|
|
|
}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
fn wobble() {
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For whatever reason; The crate author has found it imperative
|
|
|
|
|
// that `wibble` and `wobble` always be defined together.
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
```
|
2019-01-18 20:10:13 +08:00
|
|
|
|
|
|
|
|
|
#### Case study
|
|
|
|
|
[case study]: #case-study
|
|
|
|
|
|
|
|
|
|
[RFC 2500]: https://github.com/rust-lang/rfcs/pull/2500
|
|
|
|
|
|
|
|
|
|
One instance where default groups could be useful to provide a more ergonomic
|
|
|
|
|
API is to improve upon [RFC 2500]. The RFC proposes the following API:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Needle<H: Haystack>: Sized {
|
|
|
|
|
type Searcher: Searcher<H::Target>;
|
|
|
|
|
fn into_searcher(self) -> Self::Searcher;
|
|
|
|
|
|
|
|
|
|
type Consumer: Consumer<H::Target>;
|
|
|
|
|
fn into_consumer(self) -> Self::Consumer;
|
|
|
|
|
}
|
2018-08-24 01:08:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, it turns out that usually, `Consumer` and `Searcher` are
|
|
|
|
|
the same underlying type. Therefore, we would like to save the user
|
|
|
|
|
from some unnecessary work by letting them elide parts of the required
|
|
|
|
|
definitions in implementations.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
One might imagine that we'd write:
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
trait Needle<H: Haystack>: Sized {
|
|
|
|
|
type Searcher: Searcher<H::Target>;
|
|
|
|
|
fn into_searcher(self) -> Self::Searcher;
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
default {
|
|
|
|
|
type Consumer: Consumer<H::Target> = Self::Searcher;
|
|
|
|
|
fn into_consumer(self) -> Self::Consumer { self.into_searcher() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, the associated type `Searcher` does not necessarily implement
|
|
|
|
|
`Consumer<H::Target>`. Therefore, the above definition would not type check.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, we can encode the above construct by rewriting it slightly,
|
|
|
|
|
using the concept of partial implementations from [RFC 1210]:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
default impl<H: Haystack> Needle for T
|
|
|
|
|
where Self::Searcher: Consumer<H::Target> {
|
|
|
|
|
default {
|
|
|
|
|
type Consumer = Self::Searcher;
|
|
|
|
|
fn into_consumer(self) -> Self::Consumer { self.into_searcher() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Now we have ensured that `Self::Searcher` is a `Consumer<H::Target>`
|
|
|
|
|
and therefore, the above definition will type check.
|
|
|
|
|
Having done this, the API has become more ergonomic because we can
|
|
|
|
|
let users define instances of `Needle<H>` with half as many requirements.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
#### `default fn foo() { .. }` is syntactic sugar
|
|
|
|
|
|
|
|
|
|
In the section of [changes] to associated type defaults,
|
|
|
|
|
snippet (5) actually indirectly introduced default groups of a special form,
|
|
|
|
|
namely "singleton groups". That is, when we wrote:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
impl<T> Foo for Wibble<T> {
|
|
|
|
|
default type Bar = u8;
|
|
|
|
|
|
|
|
|
|
default fn quux(x: Self::Bar) -> u8 { x }
|
|
|
|
|
}
|
2018-08-26 11:16:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
this was actually sugar for:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
impl<T> Foo for Wibble<T> {
|
|
|
|
|
default {
|
|
|
|
|
type Bar = u8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default {
|
|
|
|
|
fn quux(x: Self::Bar) -> u8 { x }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We can see that these are equivalent since in the [specialization] RFC,
|
|
|
|
|
the semantics of `default fn` were that `fn` may be overridden in more
|
|
|
|
|
specific implementations. With these singleton groups, you may assume
|
|
|
|
|
the body of `Bar` in all other items in the same group; but it just
|
|
|
|
|
happens to be the case that there are no other items in the group.
|
|
|
|
|
|
|
|
|
|
#### Nesting and a tree of cliques
|
|
|
|
|
[tree of cliques]: #nesting-and-a-tree-of-cliques
|
|
|
|
|
|
|
|
|
|
In the summary, we alluded to the notion of groups being nested.
|
|
|
|
|
However, thus far we have seen no examples of such nesting.
|
|
|
|
|
This RFC does permit you do that. For example, you may write:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
|
|
|
|
default {
|
|
|
|
|
type Bar = usize;
|
|
|
|
|
|
|
|
|
|
fn alpha() -> Self::Bar {
|
|
|
|
|
0 // OK! In the same group, so we may assume `Self::Bar == usize`.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// OK; we can rely on `Self::Bar == usize`.
|
|
|
|
|
default const BETA: Self::Bar = 3;
|
|
|
|
|
|
|
|
|
|
default fn gamma() -> [Self::Bar; 4] {
|
|
|
|
|
// OK; we can depend on the underlying type of `Self::Bar`.
|
|
|
|
|
[9usize, 8, 7, 6]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// This is rejected:
|
|
|
|
|
default fn delta() -> [Self::Bar; Self::BETA] {
|
|
|
|
|
// ERROR! we may not rely on not on `Self::BETA`'s value because
|
|
|
|
|
// `Self::BETA` is a sibling of `Self::gamma` which is not in the
|
|
|
|
|
// same group and is not an ancestor either.
|
|
|
|
|
[9usize, 8, 7]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// But this is accepted:
|
|
|
|
|
default fn delta() -> [Self::Bar; 3] {
|
|
|
|
|
// OK; we can depend on `Self::Bar == usize`.
|
|
|
|
|
[9, 8, 7]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default {
|
|
|
|
|
// OK; we can still depend on `Self::Bar == usize`.
|
|
|
|
|
const EPSILON: Self::Bar = 2;
|
|
|
|
|
|
|
|
|
|
fn zeta() -> [Self::Bar; Self::Epsilon] {
|
|
|
|
|
// OK; We can assume the value of `Self::EPSILON` because it
|
|
|
|
|
// is a sibling in the same group. We may also assume that
|
|
|
|
|
// `Self::Bar == usize` because it is an ancestor.
|
|
|
|
|
[42usize, 24]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Eta;
|
|
|
|
|
struct Theta;
|
|
|
|
|
struct Iota;
|
|
|
|
|
|
|
|
|
|
impl Foo for Eta {
|
|
|
|
|
// We can override `gamma` without overriding anything else because
|
|
|
|
|
// `gamma` is the sole member of its sub-group. Note in particular
|
|
|
|
|
// that we don't have to override `alpha`.
|
|
|
|
|
fn gamma() -> [Self::Bar; 4] {
|
|
|
|
|
[43, 42, 41, 40]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Bar for Theta {
|
|
|
|
|
// Since `EPSILON` and `zeta` are in the same group; we must override
|
|
|
|
|
// them together. However, we still don't have to override anything
|
|
|
|
|
// in ancestral groups.
|
|
|
|
|
const EPSILON: Self::Bar = 0;
|
|
|
|
|
|
|
|
|
|
fn zeta() -> [Self::Bar; Self::Epsilon] {
|
|
|
|
|
[]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Bar for Iota {
|
|
|
|
|
// We have overridden `Bar` which is in the root group.
|
2022-10-08 13:46:46 +08:00
|
|
|
|
// Since all other items are descendants of the same group as `Bar` is in,
|
2019-01-18 20:10:13 +08:00
|
|
|
|
// they are allowed to depend on what `Bar` is.
|
|
|
|
|
type Bar = u8;
|
|
|
|
|
|
|
|
|
|
... // Definitions for all the other items elided for brevity.
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[clique]: https://en.wikipedia.org/wiki/Clique_(graph_theory)
|
|
|
|
|
|
|
|
|
|
In graph theory, a set of a vertices, in a graph, for which each distinct pair
|
|
|
|
|
of vertices is connected by a unique edge is said to form a [clique].
|
|
|
|
|
What the snippet above encodes is a tree of such cliques. In other words,
|
|
|
|
|
we can visualize the snippet as:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┏━━━━━━━━━━━━━━━━━┓
|
|
|
|
|
┃ + type Bar ┃
|
|
|
|
|
┏━━━━━━━━━━━━━┃ + fn alpha ┃━━━━━━━━━━━━━━┓
|
|
|
|
|
┃ ┗━━━━━━━━━━━━━━━━━┛ ┃
|
|
|
|
|
┃ ┃ ┃ ┃
|
|
|
|
|
┃ ┃ ┃ ┃
|
|
|
|
|
▼ ▼ ▼ ▼
|
|
|
|
|
┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━┓
|
|
|
|
|
┃ + const Beta ┃ ┃ + fn gamma ┃ ┃ + fn delta ┃ ┃ + const EPSILON ┃
|
|
|
|
|
┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━┛ ┃ + fn zeta ┃
|
|
|
|
|
┗━━━━━━━━━━━━━━━━━┛
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Please pay extra attention to the fact that items in the same group may
|
|
|
|
|
depend on each other's definitions as well as definitions of items that
|
|
|
|
|
are ancestors (up the tree). The inverse implication holds for what you
|
|
|
|
|
must override: if you override one item in a group, you must override
|
|
|
|
|
all items in that groups and all items in sub-groups (recursively).
|
|
|
|
|
As before, these limitations exist to preserve the soundness of the type system.
|
|
|
|
|
|
|
|
|
|
Nested groups are intended primarily expected to be used when there is one
|
|
|
|
|
associated type, for which you want to define a default, coupled with a bunch
|
|
|
|
|
of functions which need to rely on the definition of the associated type.
|
|
|
|
|
This is a good mechanism for API evolution in the sense that you can introduce
|
|
|
|
|
a new associated type, rely on it in provided methods, but still perform
|
|
|
|
|
no breaking change.
|
|
|
|
|
|
|
|
|
|
## Reference-level explanation
|
|
|
|
|
|
|
|
|
|
### Grammar
|
|
|
|
|
[grammar]: #grammar
|
|
|
|
|
|
|
|
|
|
Productions in this section which are not defined here are taken from
|
|
|
|
|
[parser-lalr.y](https://github.com/rust-lang/rust/blob/master/src/grammar/parser-lalr.y).
|
|
|
|
|
|
|
|
|
|
Given:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
trait_item : maybe_outer_attrs trait_item_leaf ;
|
|
|
|
|
|
|
|
|
|
trait_item_leaf
|
|
|
|
|
: trait_const
|
|
|
|
|
| trait_type
|
|
|
|
|
| trait_method
|
|
|
|
|
| item_macro
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
trait_const
|
|
|
|
|
: CONST ident maybe_ty_ascription maybe_const_default ';'
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
trait_type : TYPE ty_param ';' ;
|
|
|
|
|
|
|
|
|
|
trait_method : method_prefix method_common ';' | method_prefix method_provided ;
|
|
|
|
|
method_prefix : maybe_unsafe | CONST maybe_unsafe | maybe_unsafe EXTERN maybe_abi ;
|
|
|
|
|
method_provided : method_common inner_attrs_and_block ;
|
|
|
|
|
method_common
|
|
|
|
|
: FN ident generic_params fn_decl_with_self_allow_anon_params maybe_where_clause
|
|
|
|
|
;
|
|
|
|
|
```
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The production `trait_item` is changed into:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
|
|
|
|
```
|
2019-01-18 20:10:13 +08:00
|
|
|
|
trait_item : maybe_outer_attrs trait_item_def ;
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
trait_item_def
|
|
|
|
|
: trait_default_group
|
|
|
|
|
| trait_default_singleton
|
|
|
|
|
| trait_const
|
|
|
|
|
| trait_type
|
|
|
|
|
| trait_method
|
|
|
|
|
| item_macro
|
|
|
|
|
;
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
trait_default_singleton : DEFAULT trait_item ;
|
|
|
|
|
trait_default_group : DEFAULT '{' trait_item* '}' ;
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
trait_type : TYPE ty_param ('=' ty_sum)? ';' ;
|
2018-08-26 11:16:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Given:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
|
|
|
|
```
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl_item : attrs_and_vis impl_item_leaf ;
|
|
|
|
|
impl_item_leaf
|
|
|
|
|
: item_macro
|
|
|
|
|
| maybe_default impl_method
|
|
|
|
|
| maybe_default impl_const
|
|
|
|
|
| maybe_default impl_type
|
|
|
|
|
;
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl_const : item_const ;
|
|
|
|
|
impl_type : TYPE ident generic_params '=' ty_sum ';' ;
|
|
|
|
|
impl_method : method_prefix method_common ;
|
|
|
|
|
|
|
|
|
|
method_common
|
|
|
|
|
: FN ident generic_params fn_decl_with_self maybe_where_clause inner_attrs_and_block
|
|
|
|
|
;
|
2018-08-26 11:16:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The production `impl_item` is changed into:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```
|
|
|
|
|
impl_item : attrs_and_vis impl_item_def ;
|
|
|
|
|
impl_item_def
|
|
|
|
|
: impl_default_singleton
|
|
|
|
|
| impl_default_group
|
|
|
|
|
| item_macro
|
|
|
|
|
| impl_method
|
|
|
|
|
| impl_const
|
|
|
|
|
| impl_type
|
|
|
|
|
;
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl_default_singleton : DEFAULT impl_item ;
|
|
|
|
|
impl_default_group : DEFAULT '{' impl_item* '}' ;
|
|
|
|
|
```
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Note that associated type defaults are already in the grammar due to [RFC 192]
|
|
|
|
|
but we have specified them in the grammar here nonetheless.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Note also that `default default fn ..` as well as `default default { .. }` are
|
|
|
|
|
intentionally recognized by the grammar to make life easier for macro authors
|
|
|
|
|
even though writing `default default ..` should never be written directly.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Desugaring
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
After macro expansion, wherever the production `trait_default_singleton` occurs,
|
|
|
|
|
it is treated in all respects as, except for error reporting -- which is left up
|
|
|
|
|
to implementations of Rust, and is desugared to `DEFAULT '{' trait_item '}'`.
|
|
|
|
|
The same applies to `impl_default_singleton`.
|
|
|
|
|
In other words: `default fn f() {}` is desugared to `default { fn f() {} }`.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Semantics and type checking
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
#### Semantic restrictions on the syntax
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
According to the [grammar], the parser will accept items inside `default { .. }`
|
|
|
|
|
without a body. However, such an item will later be rejected during type checking.
|
|
|
|
|
The parser will also accept visibility modifiers on `default { .. }`
|
|
|
|
|
(e.g. `pub default { .. }`). However, such a visibility modifier will also be
|
|
|
|
|
rejected by the type checker.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
#### Specialization groups
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Implementations of a `trait` as well as `trait`s themselves may now
|
|
|
|
|
contain *"specialization default groups"* (henceforth: *"group(s)"*)
|
|
|
|
|
as defined by the [grammar].
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
A group forms a [clique] and is considered an *atomic unit of specialization*
|
|
|
|
|
wherein each item can be specialized / overridden.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
Groups may contain other groups - such groups are referred to as
|
|
|
|
|
*"nested groups"* and may be nested arbitrarily deeply.
|
|
|
|
|
Items which are not in any group are referred to as *`0`-deep*.
|
|
|
|
|
An item directly defined in a group which occurs at the top level of a
|
|
|
|
|
`trait` or an `impl` definition is referred to as being *`1`-deep*.
|
|
|
|
|
An item in a group which is contained in a *`1`-deep* group is *`2`-deep*.
|
|
|
|
|
If an item is nested in `k` groups it is *`k`-deep*.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
A group and its sub-groups form a *tree of cliques*.
|
|
|
|
|
Given a group `$g` with items `$x_1, .. $x_n`, an item `$x_j` in `$g`
|
|
|
|
|
can assume the definitions of `$x_i, ∀ i ∈ { 1..n }` as well as any
|
|
|
|
|
definitions of items in `$f` where `$f` is an ancestor of `$g` (up the tree).
|
|
|
|
|
Conversely, items in `$g` may not assume the definitions of items in
|
|
|
|
|
descendant groups `$h_i` of `$g` as well as items which are grouped at all
|
|
|
|
|
or which are in groups which are not ancestors of `$g`.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
If an `impl` block overrides one item `$x_j` in `$g`,
|
|
|
|
|
it also has to override all `$x_i` in `$g` where `i ≠ j` as well as
|
|
|
|
|
all items in groups `$h_i` which are descendants of `$g` (down the tree).
|
|
|
|
|
Otherwise, items do not need to be overridden.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
For example, you may write:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
trait Foo {
|
|
|
|
|
default {
|
|
|
|
|
type Bar = u8;
|
|
|
|
|
fn baz() {
|
|
|
|
|
let _: Self::Bar = 1u8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default {
|
|
|
|
|
const SIZE: usize = 3;
|
|
|
|
|
fn quux() {
|
|
|
|
|
let_: [Self::Bar; Self::SIZE] = [1u8, 2u8, 3u8];
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-26 11:16:09 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
impl Foo for () {
|
|
|
|
|
type Bar = Vec<u8>;
|
|
|
|
|
fn baz() {}
|
|
|
|
|
const SIZE: usize = 5;
|
|
|
|
|
fn quux() {}
|
2018-08-26 11:16:09 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Linting redundant `default`s
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
When in source code (but not as a consequence of macro expansion),
|
|
|
|
|
any of the following occurs, a warn-by-default lint (`redundant_default`)
|
|
|
|
|
will be emitted:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
```rust
|
|
|
|
|
default default $item
|
|
|
|
|
// ^^^^^^^ warning: Redundant `default`
|
|
|
|
|
// hint: remove `default`.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
default default {
|
|
|
|
|
// ^^^^^^^ warning: Redundant `default`
|
|
|
|
|
// hint: remove `default`.
|
|
|
|
|
...
|
|
|
|
|
}
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
default {
|
|
|
|
|
...
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
default $item
|
|
|
|
|
// ^^^^^^^ warning: Redundant `default`
|
|
|
|
|
// hint: remove `default`.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
...
|
|
|
|
|
}
|
2018-08-26 11:16:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Drawbacks
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The main drawbacks of this proposal are that:
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
1. `default { .. }` is introduced, adding to the complexity of the language.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
However, it should be noted that token `default` is already accepted for
|
|
|
|
|
use by specialization and for `default impl`.
|
|
|
|
|
Therefore, the syntax is only partially new.
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Rationale and alternatives
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Alternatives
|
2018-08-26 11:16:09 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
One may consider mechanisms such as `default(Bar, BAZ) { .. }` to give
|
|
|
|
|
more freedom as to which dependency graphs may be encoded.
|
|
|
|
|
However, in practice, we believe that the *tree of cliques* approach proposed
|
|
|
|
|
in this RFC should be more than enough for practical applications.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### `default { .. }` is syntactically light-weight
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
When you actually do need to assume the underlying default of an associated type
|
|
|
|
|
in a provided method, `default { .. }` provides a syntax that is comparatively
|
|
|
|
|
not *that* heavy weight.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
In addition, when you want to say that multiple items are overridable,
|
|
|
|
|
`default { .. }` provides less repetition than specifying `default` on
|
|
|
|
|
each item would. Thus, we believe the syntax is ergonomic.
|
|
|
|
|
|
|
|
|
|
Finally, `default { .. }` works well and allows the user a good deal of control
|
|
|
|
|
over what can and can't be assumed and what must be specialized together.
|
|
|
|
|
The grouping mechanism also composes well as seen in
|
|
|
|
|
[the section where it is discussed][default_groups].
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Tree of cliques is familiar
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
The *"can depend on"* rule is similar to the rule used to determine whether a
|
|
|
|
|
non-`pub` item in a module tree is accessible or not.
|
|
|
|
|
Familiarity is a good tool to limit complexity costs.
|
2018-08-24 01:08:55 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### Non-special treatment for methods
|
|
|
|
|
|
|
|
|
|
In this RFC we haven't given methods any special treatment.
|
|
|
|
|
We could do so by allowing methods to assume the underlying type
|
|
|
|
|
of an associated type and still be overridable without having to override
|
|
|
|
|
the type. However, this might lead to *semantic breakage* in the sense that
|
|
|
|
|
the details of an `fn` may be tied to the definition of an associated type.
|
|
|
|
|
When those details change, it may also be prudent to change the associated type.
|
|
|
|
|
Default groups give users a mechanism to enforce such decisions.
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
## Future work
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
2019-01-18 20:10:13 +08:00
|
|
|
|
### `where` clauses on `default { .. }` groups
|
2018-08-27 09:40:37 +08:00
|
|
|
|
|
|
|
|
|
From our [case study], we noticed that we had to depart from our `trait`
|
|
|
|
|
definition into a separate `default impl..` to handle the conditionality
|
|
|
|
|
of `Self::Searcher: Consumer<H::Target>`. However, one method to regain
|
|
|
|
|
the locality provided by having `default { .. }` inside the `trait` definition
|
|
|
|
|
is to realize that we could attach an optional `where` clause to the group.
|
|
|
|
|
This would allow us to write:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
trait Needle<H: Haystack>: Sized {
|
|
|
|
|
type Searcher: Searcher<H::Target>;
|
|
|
|
|
fn into_searcher(self) -> Self::Searcher;
|
|
|
|
|
|
|
|
|
|
default where
|
|
|
|
|
Self::Searcher: Consume<H::Target>
|
|
|
|
|
{
|
|
|
|
|
type Consumer: Consumer<H::Target> = Self::Searcher;
|
|
|
|
|
fn into_consumer(self) -> Self::Consumer { self.into_searcher() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The defaults in this snippet would then be equivalent to the `default impl..`
|
|
|
|
|
snippet noted in the [case study].
|
|
|
|
|
|
|
|
|
|
This `default where $bounds` construct should be able to
|
|
|
|
|
subsume common cases where you only have a single `default impl..`
|
|
|
|
|
but provide comparatively better local reasoning.
|
|
|
|
|
|
|
|
|
|
However, we do not propose this at this stage because it is unclear how
|
|
|
|
|
common `default impl..` will be in practice.
|