From c87a854bcb458783b67e39c87625742debe5d487 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Thu, 23 Aug 2018 19:08:55 +0200 Subject: [PATCH 01/13] rfc, assoc-default-groups: initial version." --- text/0000-assoc-default-groups.md | 723 ++++++++++++++++++++++++++++++ 1 file changed, 723 insertions(+) create mode 100644 text/0000-assoc-default-groups.md diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md new file mode 100644 index 00000000000..d3242ca9863 --- /dev/null +++ b/text/0000-assoc-default-groups.md @@ -0,0 +1,723 @@ +- Feature Name: `assoc_default_groups` +- Start Date: 2018-08-23 +- RFC PR: _ +- Rust Issue: _ + +# Summary +[summary]: #summary + +[RFC 192]: https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#defaults + +1. [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. + +2. [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). + +# Motivation +[motivation]: #motivation + +## For associated type defaults + +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 + + For example, we may provide an API (due to [proptest]): + + ```rust + trait Arbitrary: Sized + fmt::Debug { + type Parameters: Default = (); + + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy; + + fn arbitrary() -> Self::Strategy { + Self::arbitrary_with(Default::default()) + } + + type Strategy: Strategy; + } + ``` + + 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 + `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; + + type Strategy: Strategy; + } + ``` + + 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 = (); + + 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; + } + ``` + +## For `default { .. }` groups + +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]. + +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. + +Examples where it is useful for other items to assume the default of an +associated type include: + +[issue#29661]: https://github.com/rust-lang/rust/issues/29661 + +[comment174527854]: https://github.com/rust-lang/rust/issues/29661#issuecomment-174527854 +[comment280944035]:https://github.com/rust-lang/rust/issues/29661#issuecomment-280944035 + +1. [A default method][comment174527854] whose + [return type is an associated type:][comment280944035] + + ```rust + /// "Callbacks" for a push-based parser + trait Sink { + fn handle_foo(&mut self, ...); + + default { + type Output = Self; + + // OK to assume what `Output` really is because any overriding + // must override both `Outout` and `finish`. + fn finish(self) -> Self::Output { self } + } + } + ``` + +2. There are plenty of other examples in [rust-lang/rust#29661][issue#29661]. + +[issue#31844]: https://github.com/rust-lang/rust/issues/31844 + +3. Other examples where `default { .. }` would have been useful can be found + in the [tracking issue][issue#31844] for [specialization]: + + + + + You can see `default { .. }` being used + [here](https://github.com/rust-lang/rust/issues/31844#issuecomment-249355377). + + + + + + + + + + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +## Associated type defaults + +### Background and The status quo +[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 `::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 `::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; +} +``` + +Given snippet (4), `Self::Bar` will evaluate to `Vec`, +which is therefore the type of `::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: +`::QUUX` is of value `42u8`, but of type `Vec`. +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). + +### Changes in this RFC +[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; + +impl Foo for Wibble { + 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]. + +With these changes, +we consider the design of associated type defaults to be *finalized*. + +## `default` specialization groups +[default_groups]: #default-specialization-groups + +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 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 (6): + +```rust +trait ComputerScientist { + default { + type Bar = u8; + const QUUX: Self::Bar = 42u8; // OK! + fn wibble(x: Self::Bar) -> u8 { x } // OK! + } +} + +struct Alan; +struct Alonzo; +struct Kurt; +struct Per; + +impl ComputerScientist for Alan { + type Bar = Vec; + + // ERROR! You must override QUUX and wibble. +} + +impl ComputerScientist for Alonzo { + const QUUX: u8 = 21; + fn wibble(x: Self::Bar) -> u8 { 4 } + + // ERROR! You must override Bar. +} + +impl ComputerScientist for Kurt { + type Bar = (u8, u8); + const QUUX: Self::Bar = (0, 1); + fn wibble(x: Self::Bar) -> u8 { x.0 } + + // OK! We have overridden all items in the group. +} + +impl ComputerScientist for Per { + // OK! We have not overridden anything in the group. +} +``` + +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: + +```rust +use std::marker::PhantomData; + +trait Fruit { + type Details; + fn foo(); + fn bar(); + fn baz(); +} + +struct Citrus { species: PhantomData } +struct Orange { variety: PhantomData } +struct Blood; +struct Common; + +impl Fruit for Citrus { + 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 Fruit for Citrus> { + 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`. + let _: Self::Details = 22u8; + // ERROR! Can't assume that it's u8 either! + } +} + +impl Fruit for Citrus> { + default { + type Details = f32; + fn foo() { + let _: Self::Details = 1.0f32; // OK! + } + } +} + +impl Fruit for Citrus> { + default { + type Details = f32; + } + + fn foo() { + let _: Self::Details = 1.0f32; + // ERROR! Can't assume it is f32. + } +} +``` + +However, please note that for use of `default { .. }` inside implementations, +you will still need actual support for [specialization]. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## Grammar +[grammar]: #grammar + +The production `trait_item` is changed from: + +``` +trait_item +: trait_const +| trait_type +| trait_method +| maybe_outer_attrs item_macro +; +``` + +to: + +``` +trait_item +: trait_default +| trait_const +| trait_type +| trait_method +| maybe_outer_attrs item_macro +; + +trait_default : DEFAULT '{' (trait_item)* '}' ; +``` + +Associated type defaults are already in the grammar. + +## Semantics and type checking + +### Associated type defaults + +This section supersedes [RFC 192] with respect to associated type defaults. + +Associated types can be assigned a default type in a `trait` definition: + +```rust +trait Foo { + type Bar = $default_type; + + $other_items +} +``` + +Any item in `$other_items`, +which have any provided definitions (henceforth: *"default"*), +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. +When an associated type default exists in a `trait` definition, +it need not be specified in the implementations of that `trait`. + +This applies generally to any item inside a `trait`. +You may only assume the signature of an item, but not any default, +in defaults of other items. This also includes `impl` items for that trait. +For example, this means that you may not assume the value of an +associated `const` item in other item with a default. + +### Specialization groups + +Implementations of a `trait` as well as `trait`s themselves may now +contain *"specialization default groups"* (henceforth: *"group"*) as +defined by the [grammar]. + +Such a group is considered an *atomic unit of specialization* and +each item in such a group may be specialized / overridden. +This means that if *one* item is overridden in a group, +*all* items must be overridden in a group. + +Items inside a group may assume the defaults inside the group. +Items outside of that group may not assume the defaults inside of it. + +#### Nesting + +There applies no restriction on the nesting of groups. +This means that you may nest them arbitrarily. +When nesting does occur, the atomicity applies as if the nesting was flattened. +However, with respect to what may be assumed, the rule above applies. +For example, you may write: + +```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]; + } + } + } +} + +impl Foo for () { + type Bar = Vec; + fn baz() {} + const SIZE: usize = 5; + fn quux() {} +} +``` + +#### Linting redundant `default`s + +When in source code (but not as a consequence of macro expansion), +the following occurs, a warn-by-default lint (`redundant_default`) will be emitted: + +```rust +default { + ... + + default $item +// ^^^^^^^ warning: Redundant `default` +// hint: remove `default`. + + ... +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +The main drawbacks of this proposal are that: + +1. `default { .. }` is introduced, adding to the complexity of the language. + + 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. + +2. secondarily, if you have implementations where it is commonly 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. + + However, it is the contention of this RFC that such needs will be less common. + This is discussed below. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## Alternatives + +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 the more ergonomic approach. + +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. + +## Consistency with associated `const`s + +Consider the following valid example from stable Rust: + +```rust +trait Foo { + const BAR: usize = 1; + + fn baz() { println!("Hi I'm baz."); } +} + +impl Foo for () { + fn baz() { println!("Hi I'm () baz."); } +} +``` + +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"*. + +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. + +## Overriding everything is less ergonomic + +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. + +## `default { .. }` is syntactically light-weight + +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. + +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]. + +# Prior art +[prior-art]: #prior-art + +## Haskell + +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: + +```haskell +{-# LANGUAGE TypeFamilies #-} + +class Foo x where + type Bar x :: * + -- A default: + type Bar x = Int + + -- Provided method: + baz :: x -> Bar x -> Int + baz _ _ = 0 +``` + +In this case, we are not assuming that `Bar x` unifies with `Int`. +Let's try to assume that now: + +```haskell +{-# LANGUAGE TypeFamilies #-} + +class Foo x where + type Bar x :: * + -- A default: + type Bar x = Int + + -- Provided method: + baz :: x -> Bar x -> Int + baz _ barX = barX +``` + +This snippet results in a type checking error (tested on GHC 8.0.1): + +``` +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) +:3:1: error: +``` + +The thing to pay attention to here is: +> Couldn't match expected type ‘`Int`’ with actual type ‘`Bar x`’ + +We can clearly see that the type checker is now allowing us to assume +that `Int` and `Bar x` are the same type. +This is consistent with the approach this RFC proposes. + +To our knowledge, Haskell does not have any means such as `default { .. }` +to change this behaviour. Presumably, this is the case because Haskell +preserves parametricity and lacks specialization, +wherefore `default { .. }` might not carry its weight. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +1. Should trait objects default to the one specified in the trait if + an associated type is omitted? In other words, given: + + ```rust + trait Foo { + type Bar = usize; + fn baz(&self) -> Self::Bar; + } + + type Quux = Box; + ``` + + Should `Quux` be considered well-formed and equivalent to the following? + + ```rust + type Quux = Box>; + ``` + + This question may be left as future work for another RFC or resolved + during this RFC as the RFC is forward-compatible with such a change. From 56f229b558837f75768e235be813c4c6c98e76f2 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 26 Aug 2018 05:16:09 +0200 Subject: [PATCH 02/13] rfc, assoc-default-groups: more prior art + better reference. --- text/0000-assoc-default-groups.md | 266 ++++++++++++++++++++++++++++-- 1 file changed, 250 insertions(+), 16 deletions(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index d3242ca9863..ec8965f02d1 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -287,6 +287,29 @@ 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]. +Once place where this proposal does diverge from what is currently implemented +is with respect to the following example (6): + +```rust +#![feature(associated_type_defaults)] + +trait Foo { + type Bar = usize; + + fn baz(x: Self::Bar) -> usize; +} + +impl Foo for Vec { + 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 +allowed to assume this. To permit this is not a problem because `Foo for ()` +is not further specializable since `baz` in the implementation has not been +marked as `default`. + With these changes, we consider the design of associated type defaults to be *finalized*. @@ -300,7 +323,7 @@ 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 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 (6): +in an implementation, then all all the items must be. An example (7): ```rust trait ComputerScientist { @@ -448,7 +471,7 @@ trait_item | maybe_outer_attrs item_macro ; -trait_default : DEFAULT '{' (trait_item)* '}' ; +trait_default : DEFAULT '{' trait_item* '}' ; ``` Associated type defaults are already in the grammar. @@ -469,33 +492,41 @@ trait Foo { } ``` -Any item in `$other_items`, -which have any provided definitions (henceforth: *"default"*), +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. + 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 +and may only assume that it is `Self::TheAsociatedType`. This applies generally to any item inside a `trait`. -You may only assume the signature of an item, but not any default, -in defaults of other items. This also includes `impl` items for that 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 item with a default. +associated `const` item in other items with provided definition +in a `trait` definition. ### Specialization groups Implementations of a `trait` as well as `trait`s themselves may now -contain *"specialization default groups"* (henceforth: *"group"*) as +contain *"specialization default groups"* (henceforth: *"groups"*) as defined by the [grammar]. -Such a group is considered an *atomic unit of specialization* and -each item in such a group may be specialized / overridden. +Such a group is considered an *atomic unit of specialization* +and each item in such a group may be specialized / overridden. This means that if *one* item is overridden in a group, -*all* items must be overridden in a group. +*all* items must be overridden in that group. -Items inside a group may assume the defaults inside the group. -Items outside of that group may not assume the defaults inside of it. +Items inside a group may assume the definitions inside the group. +Items outside of that group may not assume the definitions inside of it. #### Nesting @@ -639,10 +670,13 @@ The grouping mechanism also composes well as seen in ## Haskell +[associated type defaults]: https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/at-syns.pdf + 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: +That language, being Haskell, +permits a user to specify [associated type defaults]. +For example, we may write the following legal program: ```haskell {-# LANGUAGE TypeFamilies #-} @@ -655,9 +689,16 @@ class Foo x where -- Provided method: baz :: x -> Bar x -> Int baz _ _ = 0 + +data Quux = Quux + +instance Foo Quux where + baz _ y = y ``` -In this case, we are not assuming that `Bar x` unifies with `Int`. +As in this proposal, we may assume that `y :: Int` in the above snippet. + +In this case, we are not assuming that `Bar x` unifies with `Int` in the `class`. Let's try to assume that now: ```haskell @@ -698,6 +739,199 @@ to change this behaviour. Presumably, this is the case because Haskell preserves parametricity and lacks specialization, wherefore `default { .. }` might not carry its weight. +## Idris + +[idris_interface]: http://docs.idris-lang.org/en/latest/tutorial/interfaces.html + +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 are incoherent and will +permit multiple implementations of the same interface. + +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: + +```idris +interface Iterator self where + item : Type + next : self -> Maybe (self, item) + +implementation Iterator (List a) where + item = a + next [] = Nothing + next (x :: xs) = Just (xs, x) +``` + +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: + +```idris +interface Foo x where + bar : Type + bar = Bool + + baz : x -> bar + +implementation Foo Int where + baz x = x == 0 +``` + +However, if we provide a default for `baz` in the `interface` which assumes +the default value `Bool` of `bar`, as with the following example: + +```idris +interface Foo x where + bar : Type + bar = Bool + + baz : x -> bar + baz _ = True +``` + +then we run into an error: + +``` +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 _ + +Type mismatch between + Bool (Type of True) +and + bar x _ (Expected type) +``` + +The behaviour here is exactly as in Haskell and as proposed in this RFC. + +## C++ + +In C++, it is possible to provide associated types and specialize them as well. +This is shown in the following example: + +```cpp +#include +#include + +template struct wrap {}; + +template struct foo { // Unspecialized. + typedef int bar; + + bar make_a_bar() { return 0; }; +}; + +template struct foo> { // Partial specialization. + typedef std::string bar; + + bar make_a_bar() { return std::string("hello world"); }; +}; + +int main() { + foo a_foo; + std::cout << a_foo.make_a_bar() << std::endl; + + foo> b_foo; + std::cout << b_foo.make_a_bar() << std::endl; +} +``` + +One thing to note here is that C++ allows us to assume in both the unspecialized +variant of `foo` as well as the specialized version that `bar` is the underlying +type we said it was in the `typedef`. This is unlike the default in this RFC +but the same as when we use `default { .. }`. + +Do note however that C++ will allow us to remove `make_a_bar` from the +specialized `foo>` but will then error out with (gcc 4.6.2): + +```cc +main.cpp: In function 'int main()': +main.cpp:21:24: error: 'struct foo >' has no member named 'make_a_bar' + std::cout << b_foo.make_a_bar() << std::endl; + ^~~~~~~~~~ +``` + +This shows that templates in C++ are fundamentally different from the type of +parametric polymorphism (generics) that Rust employs. + +## Swift + +[swift_assoc]: https://docs.swift.org/swift-book/LanguageGuide/Generics.html + +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: + +```swift +protocol Foo { + associatedtype Bar = Int + + func append() -> Bar +} + +struct Quux: Foo { + func baz() -> Bar { + return 1 + } +} +``` + +However, we may not write: + +```swift +protocol Foo { + associatedtype Bar = Int + + func append() -> Bar { return 0 } +} +``` + +This would result in: + +``` +main.swift:4:23: error: protocol methods may not have bodies + func baz() -> Bar { return 0 } +``` + +## Scala + +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: + +```scala +trait Foo { + type Bar = Int + + def baz(x: Bar): Int = x +} + +class Quux extends Foo { + override type Bar = Int + override def baz(x: Bar): Int = x +} +``` + +There are a few interesting things to note here: + +1. We are allowed to specify a default type `Int` for `Bar`. + +2. A default definition for `baz` may be provided. + +3. This default definition may assume the default given for `Bar`. + +4. However, we *must* explicitly state that we are overriding `baz`. + +5. If we change the definition of of `override type Bar` to `Double`, + the Scala compiler will reject it. + # Unresolved questions [unresolved-questions]: #unresolved-questions From cba554fae11d3a915ec542c07f0beac82852a820 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 00:05:19 +0200 Subject: [PATCH 03/13] rfc, assoc-default-groups: review suggestion fixes. --- text/0000-assoc-default-groups.md | 54 ++++++++++++------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index ec8965f02d1..82756f930b6 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -42,7 +42,7 @@ The following points were also noted in [RFC 192], but we expand upon them here: [proptest]: https://altsysrq.github.io/rustdoc/proptest/latest/proptest/arbitrary/trait.Arbitrary.html - For example, we may provide an API (due to [proptest]): + For example, we could change [proptest]'s API to be: ```rust trait Arbitrary: Sized + fmt::Debug { @@ -287,7 +287,7 @@ 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]. -Once place where this proposal does diverge from what is currently implemented +One place where this proposal diverges from what is currently implemented is with respect to the following example (6): ```rust @@ -306,7 +306,7 @@ impl Foo for Vec { 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 -allowed to assume this. To permit this is not a problem because `Foo for ()` +allowed to assume this. To permit this is not a problem because `Foo for Vec` is not further specializable since `baz` in the implementation has not been marked as `default`. @@ -316,14 +316,16 @@ we consider the design of associated type defaults to be *finalized*. ## `default` specialization groups [default_groups]: #default-specialization-groups +Note: Everything in this section assumes 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 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): +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 trait ComputerScientist { @@ -440,9 +442,6 @@ impl Fruit for Citrus> { } ``` -However, please note that for use of `default { .. }` inside implementations, -you will still need actual support for [specialization]. - # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -589,10 +588,9 @@ The main drawbacks of this proposal are that: use by specialization and for `default impl`. Therefore, the syntax is only partially new. -2. secondarily, if you have implementations where it is commonly 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. +2. if you have implementations where you commonly need 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. However, it is the contention of this RFC that such needs will be less common. This is discussed below. @@ -742,11 +740,12 @@ wherefore `default { .. }` might not carry its weight. ## Idris [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/ 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 are incoherent and will -permit multiple implementations of the same interface. +However, unlike Haskell and Rust, these `interface`s do not have the property +of [coherence] and will permit multiple implementations of the same interface. Since Idris is language with full spectrum dependent types, it does not distinguish between terms and types, instead, types are terms. @@ -822,13 +821,13 @@ This is shown in the following example: template struct wrap {}; template struct foo { // Unspecialized. - typedef int bar; + using bar = int; bar make_a_bar() { return 0; }; }; template struct foo> { // Partial specialization. - typedef std::string bar; + using bar = std::string; bar make_a_bar() { return std::string("hello world"); }; }; @@ -842,23 +841,10 @@ int main() { } ``` -One thing to note here is that C++ allows us to assume in both the unspecialized -variant of `foo` as well as the specialized version that `bar` is the underlying -type we said it was in the `typedef`. This is unlike the default in this RFC -but the same as when we use `default { .. }`. - -Do note however that C++ will allow us to remove `make_a_bar` from the -specialized `foo>` but will then error out with (gcc 4.6.2): - -```cc -main.cpp: In function 'int main()': -main.cpp:21:24: error: 'struct foo >' has no member named 'make_a_bar' - std::cout << b_foo.make_a_bar() << std::endl; - ^~~~~~~~~~ -``` - -This shows that templates in C++ are fundamentally different from the type of -parametric polymorphism (generics) that Rust employs. +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. ## Swift From a9ab28bd12946e0fc9555f46c4e4b8f705eb30d3 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 00:58:58 +0200 Subject: [PATCH 04/13] rfc, assoc-default-groups: more modern CS folks. --- text/0000-assoc-default-groups.md | 50 +++++++++++++++++++------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index 82756f930b6..e743e46052c 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -328,41 +328,53 @@ 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 Bar = u8; - const QUUX: Self::Bar = 42u8; // OK! - fn wibble(x: Self::Bar) -> u8 { x } // OK! + type Details = Country; + const THE_DETAILS: Self::Details = Country("Scotland"); // OK! + fn papers(details: Self::Details) -> u8 { 19 } // OK! } } -struct Alan; -struct Alonzo; -struct Kurt; -struct Per; +// https://en.wikipedia.org/wiki/Emily_Riehl +struct EmilyRiehl; -impl ComputerScientist for Alan { - type Bar = Vec; +// 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 QUUX and wibble. + // ERROR! You must override THE_DETAILS and papers. } -impl ComputerScientist for Alonzo { - const QUUX: u8 = 21; - fn wibble(x: Self::Bar) -> u8 { 4 } +impl ComputerScientist for StephanieWeirich { + const THE_DETAILS: Country = Country("USA"); + fn papers(details: Self::Details) -> u8 { 86 } - // ERROR! You must override Bar. + // ERROR! You must override Details. } -impl ComputerScientist for Kurt { - type Bar = (u8, u8); - const QUUX: Self::Bar = (0, 1); - fn wibble(x: Self::Bar) -> u8 { x.0 } +impl ComputerScientist for AndreiSabelfeld { + 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 Per { +impl ComputerScientist for ConorMcBride { // OK! We have not overridden anything in the group. } ``` From a065be92b813183fc64f3ba2551d0d46d1cdde8b Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 03:40:37 +0200 Subject: [PATCH 05/13] rfc, assoc-default-groups: case study, future work, impl trait interactions. --- text/0000-assoc-default-groups.md | 123 ++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index e743e46052c..49f858ddf06 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -454,6 +454,64 @@ impl Fruit for Citrus> { } ``` +### Case study +[case study]: #case-study + +[RFC 2500]: https://github.com/rust-lang/rfcs/pull/2500 + +One instance where specialization could be useful to provide a more ergonomic +API is to improve upon [RFC 2500]. The RFC proposes the following API: + +```rust +trait Needle: Sized { + type Searcher: Searcher; + fn into_searcher(self) -> Self::Searcher; + + type Consumer: Consumer; + fn into_consumer(self) -> Self::Consumer; +} +``` + +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. + +One might imagine that we'd write: + +```rust +trait Needle: Sized { + type Searcher: Searcher; + fn into_searcher(self) -> Self::Searcher; + + default { + type Consumer: Consumer = Self::Searcher; + fn into_consumer(self) -> Self::Consumer { self.into_searcher() } + } +} +``` + +However, the associated type `Searcher` does not necessarily implement +`Consumer`. Therefore, the above definition would not type check. + +However, we can encode the above construct by rewriting it slightly, +using the concept of partial implementations from [RFC 1210]: + +```rust +default impl Needle for T +where Self::Searcher: Consumer { + default { + type Consumer = Self::Searcher; + fn into_consumer(self) -> Self::Consumer { self.into_searcher() } + } +} +``` + +Now we have ensured that `Self::Searcher` is a `Consumer` +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` with half as much requirements. + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -525,6 +583,36 @@ 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. +#### Interaction with `existential type` + +[RFC 2071]: https://github.com/rust-lang/rfcs/blob/master/text/2071-impl-trait-existential-types.md#reference-existential-types + +[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 +in [RFC 1210]. What this entails means in concrete terms is that given: + +```rust +trait Foo { + type Assoc = impl Bar; + + ... +} +``` + +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 visibility, it is the same as that of `existential type`. + ### Specialization groups Implementations of a `trait` as well as `trait`s themselves may now @@ -953,3 +1041,38 @@ There are a few interesting things to note here: This question may be left as future work for another RFC or resolved during this RFC as the RFC is forward-compatible with such a change. + +# Future work + +## `where` clauses on `default { .. }` groups + +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`. 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: Sized { + type Searcher: Searcher; + fn into_searcher(self) -> Self::Searcher; + + default where + Self::Searcher: Consume + { + type Consumer: Consumer = 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. From e815e5ccd3789f0b2c42f9729f9756cf8928f703 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 04:52:28 +0200 Subject: [PATCH 06/13] rfc, assoc-default-groups: formatting fix. --- text/0000-assoc-default-groups.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index 49f858ddf06..2be6d171368 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -942,7 +942,7 @@ int main() { ``` 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. +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. From a714f7e6f1e7157945c9c6f1286281b2369dfe89 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 06:19:23 +0200 Subject: [PATCH 07/13] rfc, assoc-default-groups: impl Arbitrary for usize. --- text/0000-assoc-default-groups.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index 2be6d171368..61bba012120 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -79,6 +79,16 @@ The following points were also noted in [RFC 192], but we expand upon them here: } ``` + with an implementation: + + ```rust + impl Arbitrary for usize { + fn arbitrary() -> Self::Strategy { 0..100 } + + type Strategy = Range; + } + ``` + By allowing defaults, we can transition to this more flexible API without breaking any consumers by simply saying: @@ -104,6 +114,8 @@ The following points were also noted in [RFC 192], but we expand upon them here: } ``` + The implementation `Arbitrary for usize` *remains valid* even after the change. + ## For `default { .. }` groups Finally, because we are making [changes] to how associated type defaults work From 29bdcc26381af60979965f530e8a71c56decc578 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 06:23:05 +0200 Subject: [PATCH 08/13] rfc, assoc-default-groups: playground link. --- text/0000-assoc-default-groups.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index 61bba012120..1665322beeb 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -299,8 +299,10 @@ 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]. +[current_impl_diverge]: https://play.rust-lang.org/?gist=30e01d77f7045359e30c7d3f3144e984&version=nightly&mode=debug&edition=2015 + One place where this proposal diverges from what is currently implemented -is with respect to the following example (6): +is with respect to the [following example][current_impl_diverge] (6): ```rust #![feature(associated_type_defaults)] From 87679d9d279837c1612baa3688633955f48d4388 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 06:25:11 +0200 Subject: [PATCH 09/13] rfc, assoc-default-groups: type visibility -> type opacity. --- text/0000-assoc-default-groups.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index 1665322beeb..6779c20efd2 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -625,7 +625,7 @@ trait Foo { 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 visibility, it is the same as that of `existential type`. +With respect to type opacity, it is the same as that of `existential type`. ### Specialization groups From 09c122789e27b42e9e4d916151b1cb91094c0647 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 06:26:29 +0200 Subject: [PATCH 10/13] rfc, assoc-default-groups: fix typo. --- text/0000-assoc-default-groups.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index 6779c20efd2..0df1f90e906 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -645,7 +645,7 @@ Items outside of that group may not assume the definitions inside of it. There applies no restriction on the nesting of groups. This means that you may nest them arbitrarily. -When nesting does occur, the atomicity applies as if the nesting was flattened. +When nesting does occur, the atomicity applies as if the nesting were flattened. However, with respect to what may be assumed, the rule above applies. For example, you may write: From 19cf7a7bbd75e1e86a7592e2c6d031a0fb358767 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 06:45:55 +0200 Subject: [PATCH 11/13] rfc, assoc-default-groups: add unresolved question about nesting. --- text/0000-assoc-default-groups.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index 0df1f90e906..a17182c3263 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -1056,6 +1056,18 @@ There are a few interesting things to note here: This question may be left as future work for another RFC or resolved during this RFC as the RFC is forward-compatible with such a change. +2. Should groups be arbitrarily nestable? + + On the one hand, permitting arbitrary nesting is simpler from a grammatical + point of view and makes the language simpler by having *fewer rules*. + It also allows the user more fine grained control. + + On the other hand, it is not clear to what use such fine grained control + would be. Nested groups may also be less understandable and lead to confusion. + + To resolve this issue, some usage experience may be required. + Thus, it might be a good idea to defer such a choice until after the RFC. + # Future work ## `where` clauses on `default { .. }` groups From 5548ab1481db94edd30fd130e808333d5ce01237 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 18:43:57 +0200 Subject: [PATCH 12/13] rfc, assoc-default-groups: add example without assoc type. --- text/0000-assoc-default-groups.md | 37 ++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index a17182c3263..bf0e7b07d61 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -468,12 +468,44 @@ impl Fruit for Citrus> { } ``` +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() { + ... + } + + fn wobble() { + ... + } + + // For whatever reason; The crate author has found it imperative + // that `wibble` and `wobble` always be defined together. + } +} +``` + ### Case study [case study]: #case-study [RFC 2500]: https://github.com/rust-lang/rfcs/pull/2500 -One instance where specialization could be useful to provide a more ergonomic +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 @@ -641,6 +673,9 @@ This means that if *one* item is overridden in a group, Items inside a group may assume the definitions inside the group. Items outside of that group may not assume the definitions inside of it. +The parser will accept items inside `default { .. }` without a body. +However, such an item will later be rejected during type checking. + #### Nesting There applies no restriction on the nesting of groups. From 3f18a9167794b48265a68af01f7d6347d19572fd Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 27 Aug 2018 18:45:56 +0200 Subject: [PATCH 13/13] rfc, assoc-default-groups: adjust start date. --- text/0000-assoc-default-groups.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-assoc-default-groups.md b/text/0000-assoc-default-groups.md index bf0e7b07d61..efea3852198 100644 --- a/text/0000-assoc-default-groups.md +++ b/text/0000-assoc-default-groups.md @@ -1,5 +1,5 @@ - Feature Name: `assoc_default_groups` -- Start Date: 2018-08-23 +- Start Date: 2018-08-27 - RFC PR: _ - Rust Issue: _