Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Delegation #2393
Conversation
Centril
added
the
T-lang
label
Apr 6, 2018
Centril
reviewed
Apr 6, 2018
|
This RFC seems generally well done; I have some formatting nits here and there and some other "more substantive" (not nits) concerns. |
|
|
||
| In Rust, we prefer composition over inheritence for code reuse. For common cases, we make this convenient with delegation syntax sugar. | ||
|
|
||
| Whenever you have a struct S with a member field `f` of type F and F already implements a trait TR, you can delegate the implementation of TR for S to `f` using the contextual keyword `delegate`: |
This comment has been minimized.
This comment has been minimized.
|
|
||
| In Rust, we prefer composition over inheritence for code reuse. For common cases, we make this convenient with delegation syntax sugar. | ||
|
|
||
| Whenever you have a struct S with a member field `f` of type F and F already implements a trait TR, you can delegate the implementation of TR for S to `f` using the contextual keyword `delegate`: |
This comment has been minimized.
This comment has been minimized.
Centril
Apr 6, 2018
Contributor
We probably want to make delegate a real keyword due to current (one day old) lang team keyword policy that new features should be real keywords for maintenance reasons.
Here is a quick review of the breakage risk:
-
TL;DR: The risk is quite minimal and something we could probably live with.
-
Usage as ident in libstd: No
-
Usage as the name of a crate: No
-
Usage as idents in crates (sourcegraph): 19+ uses
| } | ||
| ``` | ||
|
|
||
| This is pure sugar, and does exactly the same thing as if you “manually delegated” all the items of TR like this: |
This comment has been minimized.
This comment has been minimized.
| ``` | ||
|
|
||
| Aside from the implementation of foo(), this has exactly the same meaning as the first example. |
This comment has been minimized.
This comment has been minimized.
|
|
||
| Aside from the implementation of foo(), this has exactly the same meaning as the first example. | ||
|
|
||
| If you only want to delegate specific items, rather than “all” or “most” items, then replace `*` with a comma-separated list of only the items you want to delegate. Since it’s possible for types and functions to have the same name, the items must be prefixed with `fn`, `const` and `type` as appropriate. |
This comment has been minimized.
This comment has been minimized.
Centril
Apr 6, 2018
Contributor
A good default here could be that fn is assumed (since it is most common), and so you could instead write:
impl TR for S {
delegate foo, bar, const MAX, type Item
to f;
}another possible shorthand notation could be (but I am not proposing it at this point):
impl TR for S {
delegate
fn { foo, bar, the_hulk, black_widdow },
const { MAX, MIX },
type { Item, Baz, Bar }
to f;
}but you are allowed to prefix with fn if you so wish.
|
|
||
| We expect to resolve through the RFC process before this gets merged: | ||
|
|
||
| - Is it useful and/or feasible to allow delegation statements to appear anywhere in the impl block, rather than all at the top? |
This comment has been minimized.
This comment has been minimized.
Centril
Apr 6, 2018
Contributor
My gut feeling is that it is feasible. delegate <stuff> to <field>; should be considered an item, and as such, you'd [rough sketch] first do a pass collecting all the items and after that you simply extract the delegate items and desugar those into a flat set, remove all the idents (LHS) found in the remaining set from the flat set, and then you finally merge the sets.
I think rustfmt should put them at the top, but people should be able to do as they like and the grammar should be flexible enough to accommodate that if it is technically possible.
|
|
||
| - Is it useful and/or feasible to allow delegation statements to appear anywhere in the impl block, rather than all at the top? | ||
| - Is “contextual keyword” the right term and mechanism for `delegate` and `to` as proposed here? | ||
| - Do we want to support all kinds of trait items, or should we be even more minimalist and support only methods in the first iteration? |
This comment has been minimized.
This comment has been minimized.
| - Is “contextual keyword” the right term and mechanism for `delegate` and `to` as proposed here? | ||
| - Do we want to support all kinds of trait items, or should we be even more minimalist and support only methods in the first iteration? | ||
| - Although the syntax and desugaring for "delegating some methods to one field and some to another" is straightforward, should it be postponed as a possible future extension? | ||
| - Are there any cases of [Custom Self Types](https://github.com/rust-lang/rfcs/pull/2362) where self needs to be manually dereferenced, e.g. |
This comment has been minimized.
This comment has been minimized.
| If so, can these be handled during implementation of this feature or is upfront design work required? | ||
| - There is a concern about _inherent traits_ causing duplicated symbols, can this be resolved during implementation? | ||
| - Is the possible future extension _delegate block_ ruled out? If not, keywords `delegate`/`Delegate` should be reserved in edition 2018. | ||
| - Should we implement the proposed syntax or one of the alternatives in nightly? We may wish to gain experience using a particlular syntax on nightly before committing to it. |
This comment has been minimized.
This comment has been minimized.
Centril
Apr 6, 2018
Contributor
That could be done concurrently with the RFC if someone has the time, but I don't think an experimental RFC is necessary here, the current design is pretty good.
|
|
||
| We expect to resolve through the implementation of this feature before stabilization: | ||
|
|
||
| - How does delegation interact with specialization? There will be a [default impl](https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md#default-impls) block in the future. Should we allow `delegate` to be used in a `default impl` block? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
elahn
commented
Apr 6, 2018
|
In the previous revision:
These questions are considered resolved unless someone objects. |
scottmcm
reviewed
Apr 7, 2018
| impl AddAssign for AddOnlyCounter { | ||
| delegate * to 0; | ||
| } |
This comment has been minimized.
This comment has been minimized.
scottmcm
Apr 7, 2018
•
Member
I was already thinking of suggesting that it be delegate <list> to self.<field>, and this solidified the feeling. (delegate fn hash to 0 looks like it'd do 0.hash() to me.) I like there being restrictions as a starting point, but I wouldn't be surprised at all for this to get expanded, and expanding to expressions (perhaps a subset thereof, but note that unimplemented!() would just work if it took anything) seems like the most obvious one, so I think the syntax should at least look like a normal expression from the beginning. (Readers: feel free to
Edit: Oh, I see the mention of this in the alternatives. I'm not convinced learnability is a big problem; even with just a field name it seems like it's "oh, they'll put self. in front" and expect more things to work, and it'd be easy to give a very clear error for "delegation only supports a single field". More abstractly, is there a reason it couldn't support an arbitrary expression? There's already a "implementer expression" concept in the reference section below. (Well, ones where let _ = expr; could infer the type, giving the same "cannot infer type for T" errors you get from things like that let if you tried to do something like delegate * to self.0.into();.)
This comment has been minimized.
This comment has been minimized.
Centril
Apr 7, 2018
Contributor
Personally, I'm quite torn on the subject of self vs. not. While self.field is clearer, it is also a bit more verbose; and you could support arbitrary expressions with a { } enclosing of { unimplemented!() }.
With respect to error messages and learnability I agree that this isn't a big concern.
scottmcm
reviewed
Apr 7, 2018
|
|
||
| Delegation must be to a field on `Self`. Other kinds of implementer expressions are left as future extensions. This also means delegation can only be done on structs for now. | ||
|
|
||
| There may be more than one delegation statement. For readability, `rustfmt` moves delegation statements to the top of an impl block. |
This comment has been minimized.
This comment has been minimized.
scottmcm
Apr 7, 2018
•
Member
Perhaps justify why this is more readable? I can see an argument for *, but especially for single-method delegation, I'm not convinced. Particularly in a case where I'm changing existing code to replace manual delegation with delegate, I'd expect rustfmt to leave the the item where it is so the diff is the obvious one. And even in new code, I might be intentionally matching the order of the methods on the trait, for example. (Nit-picky: if it's going to move multiple, it would need to pick an order to put them in.)
This comment has been minimized.
This comment has been minimized.
Centril
Apr 7, 2018
•
Contributor
My current thinking here is that:
- The argument starts from
*, which should be at the top so that it is seen first - For consistency with
*, you place all delegation wheredelegate * to whateverwould be
An argument could however be made that all delegations should be at the bottom since they often will be delegate * and so they are a sort of "catch the rest", i.e, they function like match x { important => .. , _ => .. } does wrt. _ =>.
With respect to order, I'd first group items by item type and then in each group alphabetically so:
- const
- type
- fn
This comment has been minimized.
This comment has been minimized.
scottmcm
Apr 8, 2018
Member
BTW, does rustfmt reorder any other items? I tried on play and it doesn't seem to reorder type and fn in an impl block, for example...
This comment has been minimized.
This comment has been minimized.
Centril
Apr 8, 2018
Contributor
Interesting!
PS: we could leave formatting bikeshed up to a style-fmt RFC.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
What happens when things go wrong? For example, what happens if I try to impl Default for AddOnlyCounter {
delegate * to 0;
}Is there a way to map output (and output types)? For example, if I do impl Add for AddOnlyCounter {
delegate * to 0;
}It seems like the output type is most likely In general, it seems like |
scottmcm
reviewed
Apr 7, 2018
| ```rust | ||
| fn check_name(&self, name: &str, ignore_capitals: bool, state: &mut State) -> bool { | ||
| self.f.check_name(&name, ignore_capitals, &mut state) | ||
| } |
This comment has been minimized.
This comment has been minimized.
scottmcm
Apr 7, 2018
Member
I don't think I know what "according to their type" means here. Why are the extra &s needed, instead of just moving them all? Perhaps self.f.check_name({name}, {ignore_capitals}, {state}).
Centril
reviewed
Apr 7, 2018
| , MonadWriter Unique ) | ||
| ``` | ||
|
|
||
| This is massive code reuse and not in any OOP language ^,- |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Yes, please (ignoring specific syntax). |
tanriol
reviewed
Apr 7, 2018
| impl TR for S { | ||
| delegate * to field_one; | ||
| delegate fn foo, const MAX, type Item | ||
| to field_two; |
This comment has been minimized.
This comment has been minimized.
tanriol
Apr 7, 2018
Would be nice to have a bit more motivation for this. Treating a trait as a unit of behavior, in what situations does it make sense to delegate some behavior to one field and some to a different one?
jan-hudec
reviewed
Apr 7, 2018
|
|
||
| Many of these syntaxes were never “rejected” in the original RFC’s comment thread and are likely still on the table. This list merely describes the authors' rationale for preferring `delegate ... to field_name;` over all of these alternatives. | ||
|
|
||
| - `impl TR for S use self.F { ... }` was criticized in the first RFC’s comment thread for looking too much like inheritance. |
This comment has been minimized.
This comment has been minimized.
jan-hudec
Apr 7, 2018
Why is that a problem? It basically is inheritance. Or a restricted version of it, because inheritance delegates all methods to the designated member called “base” while this only delegates methods of specific trait. But that restriction is obvious from this syntax.
This comment has been minimized.
This comment has been minimized.
Ixrec
Apr 7, 2018
•
Contributor
"Inheritance" traditionally means a LOT more than simply delegating implementations to another type. For instance: committing to the same interface, having the same internal structure as another type, or being implicitly convertable to other types. In Rust those things are usually kept separate, which we'd like to keep doing.
So if we wanted to make this part of the RFC clearer, we could say that impl TR for S use self.F {} makes it surprising that a field named F must already exist in S's declaration, rather than being implicitly added by this impl TR for S use self.F {}. Or we could say that impl TR for S use self.F {} makes it surprising that S cannot be implicitly converted to an F. And so on.
I don't personally think there's any one or two specific features in the "inheritance" grabbag that most users would mistakently expect when they see impl TR for S use self.F {}, but I do think a lot of people would mistakenly expect something on top of method delegation if they saw that syntax. It might be that "it looks too much like inheritance" is the only concise way to present that argument without getting into the weeds of what people think "inheritance" means.
This comment has been minimized.
This comment has been minimized.
jan-hudec
Apr 8, 2018
Inheritance does not mean all that much more than delegation, really. It does mean that all interfaces will be delegated, but it should be obvious it's not happening here, because it starts by stating which trait it is delegating. It does not mean committing to the same structure, only to containing that structure, but that needs to happen here as well. The only other thing inheritance does is the implicit coercion of reference. That won't happen here, but we only said we are delegating specific trait after all. And coercion to that trait does work.
This form would not be appropriate for delegating inherent methods, because that would look like it might be doing more than it does. But for the traits, it would be convenient shortcut that does not really look like promising more than it does.
makes it surprising that a field named
Fmust already exist inS's declaration
Since it does not mention the type of F, it's quite clear that that still has to be given.
makes it surprising that
Scannot be implicitly converted to anF
I would somewhat agree for inherent methods, i.e. impl S use self.F {}. But I wouldn't fear that much for the impl TR for S case.
This comment has been minimized.
This comment has been minimized.
jan-hudec
Apr 8, 2018
Hm, but there is one difference that I have to admit would be quite confusing—in case of inheritance, the overridden behaviour applies even if you've got a reference to the “base field”. But in our case it won't. In fact, I fear delegating some methods, but not all of them, will be confusing with any syntax.
This comment has been minimized.
This comment has been minimized.
|
It's possible I missed it, but there's no mention of delegating to nested fields, eg. |
This comment has been minimized.
This comment has been minimized.
I still prefer this syntax proposed by @eddyb . This syntax does not introduce new keyword (contextual or not). And the meaning is obvious, and "feels" just right. I would argue that we should not encourage "glob" delegations. I am wondering how many cases are left for "glob" if we can use |
Nemo157
reviewed
Apr 8, 2018
| ```rust | ||
| impl TR for S { | ||
| fn foo(self: Box<Self>) -> u32 { | ||
| self.f.foo() |
This comment has been minimized.
This comment has been minimized.
Nemo157
Apr 8, 2018
•
Contributor
I don’t believe the linked Custom Self Types RFC adds support for implicit projections of smart pointers (I don’t think it is possible in general, given an Rc<T> you can’t obtain an Rc<SomeFieldOfT>), which would be necessary for this to work.
This comment has been minimized.
This comment has been minimized.
Do you mean "glob delegation" as in any syntax to delegate everything not explicitly implemented, or just the use of |
This comment has been minimized.
This comment has been minimized.
|
@Ixrec the RFC states the shortcoming of
|
This comment has been minimized.
This comment has been minimized.
leodasvacas
commented
Apr 8, 2018
|
I'm happy that there are people working on this as I find the motivation compelling. But one of the reasons #1406 was closed was that it tried to do too much, with too much syntax. I think this RFC still has that same problem. Personally the only feature I want is to delegate an entire trait implementation to a field. An attribute would do fine, I see no need for first-class syntax. |
This comment has been minimized.
This comment has been minimized.
|
@leodasvacas To clarify, are you only talking about the introduction of dedicated syntax, or do you also feel this RFC "does too much"? It sounds like you're saying both, but the actual functionality proposed here (i.e., ignoring all of the possible future extensions) is a small subset of what #1406 proposed and under-specified. And part of the reason #1406 was closed is because its proposed reuse of an existing keyword was potentially confusing and ambiguous; now that we have epochs/editions introducing a new keyword is intended to be an improvement on it (if you have a specific alternative to a new keyword without the problems of #1406 I'd love to hear it). @WiSaGaN I honestly can't figure out what you're trying to say in that comment. Are you objecting to the feature of delegating all items? Or the feature of delegating most/all non-explicitly implemented items? Or some specific syntax for either or both of those? I believe |
This comment has been minimized.
This comment has been minimized.
|
@Ixrec We may be able to use |
This comment has been minimized.
This comment has been minimized.
|
One other argument in favour of using impl<'a, T: 'a + Trait> Trait for &'a T {
delegate * to *self;
}Which, IMHO, should be allowed from the start. So many traits do this and I think it's extremely reasonable to include it. Additionally, disallowing I think that it should be clarified that this syntax should not alter the behaviour compared to the manual equivalent. To the point where, if they differ, it should be considered a compiler bug. This goes either way; if something works with delegation (as it should) when it fails manually, I'd consider it a bug. |
This comment has been minimized.
This comment has been minimized.
|
Could I delegate struct MyStruct {
field_a_ignore_me: Foo,
field_b: u8,
field_c: String,
}
impl Ord for MyStruct {
delegate * to |&self| (self.field_b, self.field_c);
} |
This comment has been minimized.
This comment has been minimized.
|
@kornelski No. That probably falls under this item on the list of possible future extensions:
|
This comment has been minimized.
This comment has been minimized.
|
Nit: This is proposing a |
This comment has been minimized.
This comment has been minimized.
elahn
commented
Apr 12, 2018
A nice compiler error explaining
Since we're delegating to impl Add for AddOnlyCounter {
type Output = <u32 as Add>::Output;
fn add(self, other: u32) -> u32 { self.0.add(other) }
}To map the output type using the possible future extension "delegate block": impl Add for AddOnlyCounter {
type Output = AddOnlyCounter;
delegate * {
|self| self.0
} -> {
|delegate| AddOnlyCounter { delegate }
}
}
// Generated code:
impl Add for AddOnlyCounter {
type Output = AddOnlyCounter;
fn add(self, other: u32) -> AddOnlyCounter {
AddOnlyCounter { self.0.add(other) }
}
}For RHS to be |
This comment has been minimized.
This comment has been minimized.
mehcode
commented
Apr 16, 2018
•
|
I think I'm missing the point here. Couldn't this be trivially provided by a crate and a proc macro for the common case? Does it really need to be syntax? #[delegates(Trait, "bar")]
struct Foo { bar: Bar } |
This comment has been minimized.
This comment has been minimized.
|
@mehcode the proc macro would have to have the ability to tell what methods |
This comment has been minimized.
This comment has been minimized.
|
Meta-comment: I definitely want some form of delegation, but I think this RFC could still do a better job of carving out a simple and uncontroversial subset. For example, when I think of delegating First Draft: Only methods that don't mention
Umm, |
This was referenced Jul 1, 2018
This comment has been minimized.
This comment has been minimized.
huangjj27
commented
Jul 9, 2018
•
|
How about making
the drawback is that it needs to add new rules for and there are some rules of these delegating
|
This comment has been minimized.
This comment has been minimized.
|
@huangjj27 In past discussions this has been rejected because it'd be easily confused with regular |
Centril
assigned
cramertj
Aug 9, 2018
Centril
referenced this pull request
Oct 7, 2018
Closed
RFC: allow delegating some methods from an trait impl to a field of a struct #292
Centril
added
A-syntax
A-traits
A-delegation
A-impls
labels
Nov 22, 2018
This comment has been minimized.
This comment has been minimized.
burdges
commented
Dec 13, 2018
•
|
I love the proposal, except..
All these "items" should use
and the freestanding
I wrote We should think more about this multi-receiver type business too. Is this "macro-like reciver polymorphism" sensible elsewhere? I think so, although not always advisable. If we want it elsewhere, then is |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 17, 2018
|
While I think the motivation and intention for this feature are good, I am kind of worried about what happens when you want to change something from a normal fn (that may be delegation plus some minor logic) to simple delegation, and vice versa. The syntaxes are completing the different. This is essentially the reason why if-statements without curly brackets have typically been proscribed in C-like languages, and the pain point here is even worse I reckon. |
This comment has been minimized.
This comment has been minimized.
|
@alexreg Are you concerned about ergonomics or about diff size? Could you give an example of a problematic case? |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 17, 2018
|
@mark-i-m Ergonomics. |
This comment has been minimized.
This comment has been minimized.
This does not concern me. Some examples from Ruby, where I use delegation relatively frequently. I've picked the two variants I see the most and squashed the code around a bit to make them comparable: class Example
def initialize(sandwich_maker)
@sandwich_maker = sandwich_maker
end
## By hand
def make_me_a_sandwich
sandwich_maker.make_me_a_sandwich
end
## Forwardable
extend Forwardable
def_delegators :@sandwich_maker, :make_me_a_sandwich
## ActiveSupport
require 'active_support/core_ext/module/delegation'
attr_reader :sandwich_maker
delegate :make_me_a_sandwich, to: :sandwich_maker
endOn the flip side, I think I'd prefer visually distinct versions because they catch my eye and let me know that "nothing special is going on here, just forwarding the call, thanks". Ergonomics-wise, I think the big thing is that delegating a single method needs to be shorter than writing it out. A basic Rust function is going to be 3 lines, so ideally we can opt-in to delegation with zero or one line of overhead (the |
This comment has been minimized.
This comment has been minimized.
|
This also makes me realize that I'd like to see some discussion of renaming. A shortened example from this years Advent of Code: struct Game {
board: Board,
last_board: Board,
}
impl Game {
fn pieces_left(&self) -> usize {
self.last_board.pieces()
}
}
struct Board(Vec<u8>);
impl Board {
fn pieces(&self) -> usize {
self.0.len()
}
}I'd like to see this supported (using some strawman syntax): struct Game {
board: Board,
last_board: Board,
}
impl Game {
delegate pieces to self.last_board as pieces_left;
}
struct Board(Vec<u8>);
impl Board {
delegate len to self.0 as pieces;
} |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 18, 2018
Fair enough, but I'd at least like to see more similar syntax. The difference shouldn't be so blatant, for the reasons of both false expectations and moreover ergonomics. |
This comment has been minimized.
This comment has been minimized.
I don't follow what you mean by either "false expectations" or "ergonomics" in this case. Could you provide concrete examples of what each means? |
This comment has been minimized.
This comment has been minimized.
|
Why not use symbols instead of words? i.e. |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 18, 2018
For ergonomics: I'd like to see a syntax somewhat like this used for delegation, if we decide to support it at all: impl<'a> Hash for H<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
delegate_to!(self.name);
}
}It would be nice for this to be a proc macro even, rather than a language extension. By "false expectations", I just mean that the syntax proposed in the RFC is far away from the usual function definition syntax, so not immediately obvious a function is being defined. Overall though, I'm still not convinced that the utility feature adds to the language outweighs the expanded surface area/complexity of syntax. |
This comment has been minimized.
This comment has been minimized.
olson-sean-k
commented
Dec 19, 2018
I think a language extension could be very useful for this. Repeating functions can be extremely burdensome, even if there are just a handful of them. That kind of friction makes anti-patterns like the In an extreme example, I have a crate with types that nest four levels deep in order to take ownership of different data and prevent clients from performing arbitrary mutations. A single top-level type that is part of the public API then delegates to a field and so on. In this case, delegating using macros still leads to 100s of lines of code, as delegation (re)-occurs at each level and each level exposes as many as 10 functions. I'm hoping for a way to manage this that avoids anti-patterns like the |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 19, 2018
|
You still ignore the issue of ergonomics though. |
This comment has been minimized.
This comment has been minimized.
Many people have tried to do this (including myself) since probably before Rust 1.0. It cannot be done in a macro because a macro does not have access to the arguments / types of an arbitrary type or trait. That is, a macro cannot say
I'd claim that your suggested syntax is the one that ignores ergonomics, as it means I need to know the types of the function's arguments/return value; to me, being able to ignore that is a huge point of delegation.
This is true, but I'm OK with calling something delegation (or one of the synonyms discussed upthread) and expecting a programmer to know what "delegation" means or to look it up. A semi-related example is implementing a trait: impl Iterator for MyType {
type Item = ();
fn next(&mut self) -> Option<()> { None }
}There are zero characters here that indicate that the functions A weird in-between form of the strawman explicit delegation syntax applied to the "magically occurring iterator functions" could be: impl Iterator for MyType {
type Item = ();
fn next(&mut self) -> Option<()> { None }
fn count;
fn map;
// etc...
}But I think people would balk at this, for "ergonomic" reasons. |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 19, 2018
|
You make a fair point... when I said "ergonomics" above, however, I meant the ergonomics of switching between a delegated method and a normal one. It involves completely removing one line and adding more lines. The function header syntax isn't shared, or such. That's what I'm getting at. I suppose some sacrifice has to be made for the ergonomics of writing such delegations in the first place though... |
This comment has been minimized.
This comment has been minimized.
jan-hudec
commented
Dec 27, 2018
|
@alexreg, I don't think that's the right kind of ergonomics in this case though. Delegation is useful when you can delegate a lot of methods to a member with very little typing. Once you have to write out the signatures, there is nothing to be saved any more and the feature becomes useless. Compare
with the current code
What do you save with the former, again? Ergonomic in this case means easy to delegate a lot of things with little typing. This is most ergonomic:
with this being almost as good:
Remember, the Haskell equivalent is just generalized deriving. Even this is more explicit than the Haskell version stating to which field we are delegating—Haskell simply delegates to whichever field is of type that is in requested type class. The syntax in the current proposal,
is really on the edge of being too verbose (can be shortened with a macro). Anything more verbose severely reduces the usefulness of the feature. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Dec 28, 2018
|
We could use an unstable variant of the syntax in this RFC to provide a stable field oriented syntax, ala
There are however several iffy RFCs like this that exist primarily because proc macros cannot really access type level information. We might therefore investigate ways for proc macros to delay themselves until type checking begins and then query the type checker. I'd worry doing that might hurt compile times, but it might yield a far cleaner language. |
This comment has been minimized.
This comment has been minimized.
alexreg
commented
Dec 28, 2018
|
@jan-hudec You (and others) are starting to win me over... let's see where this goes. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jan 1, 2019
|
An interesting trick for a custom test framework #2318 would be verifying that some specializations rust-lang/rust#31844 produced the same results as less specialized impls. I think delegations are about the only way I can imagine doing this nicely. |
elahn commentedApr 6, 2018
•
edited
Syntax sugar for efficient code reuse via the composition pattern. Wrapper functions are generated for a struct, delegating most or all of a trait’s
implblock to a member field that already implements the trait.Rendered
Please Note:
This RFC is a group effort from the Rust community. Whenever an issue is raised, please edit the RFC draft to address it as best you can.
If the design needs to be bikeshedded, please do so on this internals thread.
Whenever an issue or question has been resolved, please submit a PR to this RFC.
Thank you, everyone for your contributions, they’ve been a big help. If we continue this collaborative style throughout the RFC process, I’ve no doubt we can address any concerns that arise and get this puppy accepted!