Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRfc: delegation of implementation #1406
Conversation
withoutboats
reviewed
Dec 13, 2015
| ## Associated types/constants | ||
| Unless explicitly set associated types and constants should default to the surrogate implementation value of the corresponding items. |
This comment has been minimized.
This comment has been minimized.
withoutboats
Dec 13, 2015
Contributor
How could a type explicitly set associated types different from the surrogate implementation without introducing a type error?
This comment has been minimized.
This comment has been minimized.
contactomorph
Dec 13, 2015
Author
It will certainly introduce a type error. But if you need diverging associated types there's a high chance you want something more complex than delegation anyway. In that case you will certainly have to provide a specific implementation for most of your methods. The proposed feature is only worth it if the delegating type has a behaviour "similar to" the surrogate type. The more it diverges the less valid the entire approach becomes.
An alternative could be to allow additional delegating expressions for associated types. This would create a bridge between DelegatingType::AssociatedType and SurrogateType::AssociatedType and enable the compiler to transform one type into another where required. But in the end I felt that the situations where this would be the expected behaviour might be so specific that I didn't mention that possibility.
withoutboats
reviewed
Dec 13, 2015
|
|
||
| Rust has no inheritance (yet) and as a result composition is an even more interesting pattern for factoring code than in other languages. In fact it is already used in many places. Some (approximate) figures: | ||
|
|
||
| Project | Occurences of "delegating methods" | |
This comment has been minimized.
This comment has been minimized.
withoutboats
Dec 13, 2015
Contributor
Percentages would be much more useful than numbers here, I don't know what these values mean without a referent.
This comment has been minimized.
This comment has been minimized.
contactomorph
Dec 13, 2015
Author
Right. These figures may not be that useful in the end. My purpose was just to show that the composition pattern is already used in rust projects. I'm sure the percentages are small. (But I do not think it invalidates the proposal: composition is currently handicaped by its heavy syntax so it may not be used as often as it could be)
withoutboats
reviewed
Dec 13, 2015
| ## Compiler plugin | ||
| I was suggested to write a compiler plugin. But I was also told that [type information is not accessible][type_information] (unless you can annotate the surrogate type yourself, which implies you must own it). Moreover I'm not sure a plugin could easily solve the partial delegation cases. |
This comment has been minimized.
This comment has been minimized.
withoutboats
Dec 13, 2015
Contributor
I don't think a macro requires type information any more than #[derive] does (and it doesn't). If you have a macro like #[delegate(Trait => member)] or whatever, it can generate an impl with the body of each method calling that method on self.member.
More complex cases would be more difficult to implement, of course, but they don't introduce any dependence on type information. I think even if this were built into the language, it would be sugar that would be implemented in AST -> HIR, without type information.
This comment has been minimized.
This comment has been minimized.
Stebalien
Dec 13, 2015
Contributor
The problem is that, as far as I know, there is (currently) no way to lookup Trait in a compiler plugin so there's no way to know what methods need to be implemented.
This comment has been minimized.
This comment has been minimized.
withoutboats
Dec 13, 2015
Contributor
True. Hopefully when syntax extensions are revisited this will be addressed.
This comment has been minimized.
This comment has been minimized.
Stebalien
Dec 13, 2015
Contributor
Unfortunately, this would require two macro expansion phases (one for macros that can add new namable items and one for macros that can lookup named items).
This comment has been minimized.
This comment has been minimized.
|
How does (partial) delegation interact with default impls?
If Either choice leads to potentially confusing bugs; so maybe we shouldn't support partial delegation, but only full delegation? Are there any other potentially problematic interactions with other features (e.g. specialization)? Also, you should separate the possible extensions ("Inverse delegating expressions", "Combined delegation", "Function-based delegation", "More complex delegation") from the detailed design. I think most of those are not worth the additional complexity. But the core feature idea, |
This comment has been minimized.
This comment has been minimized.
leodasvacas
commented
Dec 13, 2015
|
Thanks for writing this RFC, I agree with the concerns of inheritance overuse, this makes great use of the trait system |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Dec 13, 2015
|
Very nice. I doubt partial delegation makes sense, as it gets you into inheritance issues, but maybe that's a use case for function delegation, assuming it makes sense. I donno if this ad hoc function delegation is wise either, but if if so maybe functions that could be delegated to could be marked as such, basically making them an anonymous trait. Or maybe this should be done with default impls anyways. Also, combined delegation looks harmless. It's worth being more explicit that this deals with structs and tuple structs (newtypes), but apparently does not cover anonymous tuples, which it might, and maybe say why. Around enums, one might mention in Alternates that deriving mechanisms could potentially do enums, but they are likely heavier and remain an ongoing topic of research in the functional language community : https://stackoverflow.com/questions/3864647/how-does-deriving-work-in-haskell/3864801#3864801 |
This comment has been minimized.
This comment has been minimized.
Done.
That's a good question. I think the most reasonable approach is to give delegation higher precedence over default impls and specialization in all situations. This rule is simple to understand and IMHO the most compatible with the principle of least astonishment.
To me it is the equivalent of the possibility to override a virtual method in a subclass in the OOP world so I'm not sure on which aspects it could be considered as an issue.
I focused on examples found in existing code which mainly deal with structs. I may miss something but I see no reason why this feature should be limited to a certain kind of type. Basically what you need is something that can support implementations (any type) and an expression to "convert" a type |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Dec 13, 2015
|
In that case, a delegation for an |
This comment has been minimized.
This comment has been minimized.
Of course. That might not be a very good idea for readability reasons and a preferred approach would certainly be to encapsulate your match inside a function that would be called in the delegating expression. But it should be possible.
I'm not sure exactly about the issue. If the variants of your enum are actually quite different, it naturally implies there is very little chance you can identify a single surrogate type that would make sense. So just don't use delegation at all in that case. However this is a consequence of the semantics of your objects (what your enum and your trait represent) not an issue with the compatibility of delegation and enums. Now if you realize for example that a given enum |
This comment has been minimized.
This comment has been minimized.
|
The simple delegation case could be handled by the The derive syntax that could be used can be bikeshedded of course, but simply putting the expression after the type should be readable. #[derive(PartialEq("self.1.abs()")]
struct A(i32, i64);About partial delegation: that sounds more like you actually should split your trait into multiple traits. |
This comment has been minimized.
This comment has been minimized.
Your remark implies that you owns the trait and can modify it as you want. That's not necessarily true. |
This comment has been minimized.
This comment has been minimized.
neither am I, but that's currently the only available syntax. Caveat: you can do this in a crate instead of the standard library by using a different name than
True, but my statement still stands, the owner should split it. Rust should not have syntax just for working around bugs in foreign code. |
This comment has been minimized.
This comment has been minimized.
Not with the current macro system. As I stated earlier, you would need two macro expansion phases. |
This comment has been minimized.
This comment has been minimized.
anmej
commented
Dec 14, 2015
|
May I suggest using |
This comment has been minimized.
This comment has been minimized.
|
@anmej How about |
This comment has been minimized.
This comment has been minimized.
anmej
commented
Dec 14, 2015
|
I assume the devs don't want to add new keywords. |
This comment has been minimized.
This comment has been minimized.
|
I was reminded of C++11's constructor inheriting. impl<'a> Hash for H<'a> {
use self.name impl;
// can "replace"/override certain method impls
}Or make it By the way, what about enabling the adoption of all of the implementations, e.g. if impl<Rhs> PartialOrd<Rhs> for Thing {
use self.name impl;
}If we only want certain ones then we can write them out explicitly (unless some shorter syntax is proposed, though I'm not necessarily eager to come up with one): // adopt PartialOrd<Foo> and PartialOrd<Bar> implementations
impl PartialOrd<Foo> for Thing {
use self.name impl;
}
impl PartialOrd<Bar> for Thing {
use self.name impl;
} |
This comment has been minimized.
This comment has been minimized.
camlorn
commented
Dec 15, 2015
|
As someone currently using C++ and considering Rust for future projects, this looks like it could solve almost all the places I'd actually want to use inheritance. For what it's worth, something like this would go a long way towards attracting me to the language. Definitely +1. |
nrc
added
the
T-lang
label
Dec 16, 2015
This comment has been minimized.
This comment has been minimized.
I have no strong opinion on the exact syntax as long it is short and consistent. I guess not introducing yet another keyword would be desirable.
This is good news. At the same time it shows there is a urgent need for better factoring features before the adoption of inheritance. |
This comment has been minimized.
This comment has been minimized.
|
So my major concern is that it seems very difficult to produce useful error message. If the 300 issues tagged with A-diagnostics (the most-used tag) on rust-lang/rust are anything to go by, error messages are frequent source of confusion and problems. And this taking into account that many people praise the high quality of Rust's errors! |
aturon
self-assigned this
Jan 7, 2016
This was referenced Jan 18, 2016
shepmaster
reviewed
Jan 22, 2016
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| Provide a syntactic sugar to automatically implement a given trait `Tr` using a pre-existing type implementing `Tr`. The purpose is to improve code reuse in rust without damaging the orthogonality of already existing concepts or adding new ones. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
This seems like a great step forward, and I am a big fan of encouraging composition over inheritance. Similar to the concerns about inheritance, I think that I've also started on a compiler plugin to do the same thing and quickly ran into similar issues. You simply don't know the relevant types at the right time of the process. I think that would be the same problem with a @Aatch do you have any specific issues in mind? Conceptually, I'd hope that the errors would basically be the same as if we had a macro that expanded to the |
globulomorph
added some commits
Jan 25, 2016
behnam
added a commit
to open-i18n/rust-unic
that referenced
this pull request
Aug 8, 2017
behnam
added a commit
to open-i18n/rust-unic
that referenced
this pull request
Aug 8, 2017
behnam
added a commit
to open-i18n/rust-unic
that referenced
this pull request
Aug 8, 2017
CAD97
referenced this pull request
Aug 8, 2017
Merged
[char_property] Implement *CharProperty traits #89
behnam
added a commit
to open-i18n/rust-unic
that referenced
this pull request
Aug 8, 2017
behnam
added a commit
to open-i18n/rust-unic
that referenced
this pull request
Aug 9, 2017
behnam
added a commit
to open-i18n/rust-unic
that referenced
this pull request
Aug 9, 2017
CAD97
added a commit
to CAD97/rust-unic
that referenced
this pull request
Aug 10, 2017
This comment has been minimized.
This comment has been minimized.
@cramertj Fine. Then my opinion is that the syntax should support these features:
As usual a dedicated keyword is of course clearer (the
@eddyb I would find it pretty obscure not to have similar syntaxes for two variations on the same idea. Additionally the second proposition does not extend very nicely to a list of methods. Why not
|
This comment has been minimized.
This comment has been minimized.
|
@contactomorph could you consider signing off to the MIT/Apache 2.0 licensing terms inside this issue: #2096 ? Thanks! |
This comment has been minimized.
This comment has been minimized.
|
@aturon @eddyb @nikomatsakis @nrc @pnkfelix @withoutboats would it be possible to add an "Ergonomics Initiative" lavel to this Pull request ? Thank you |
petrochenkov
added
the
Ergonomics Initiative
label
Aug 24, 2017
This comment has been minimized.
This comment has been minimized.
|
I'm going to move to postpone this RFC. While this remains an important problem the lang team would like to tackle, there remain blocking issues with the RFC as proposed. An effort was started to develop a new, more conservative design, and we'd very much love to see that come to fruition, but at this point it seems highly unlikely to land before the impl period. Thanks, @contactomorph and many, many others for continuing to push on this. @rfcbot fcp postpone |
rfcbot
added
the
proposed-final-comment-period
label
Aug 31, 2017
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 31, 2017
•
|
Team member @aturon has proposed to postpone this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
rfcbot
added
final-comment-period
and removed
proposed-final-comment-period
labels
Sep 19, 2017
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Sep 19, 2017
|
|
This comment has been minimized.
This comment has been minimized.
|
I'm going to go ahead and close this out as postponed, given that the impl period has started. I hope folks can pick this back up toward the end of the year! |
aturon
closed this
Sep 20, 2017
This comment has been minimized.
This comment has been minimized.
Kixunil
commented
Sep 20, 2017
|
|
burdges
referenced this pull request
Dec 22, 2017
Merged
Unnamed fields of struct and union type #2102
This comment has been minimized.
This comment has been minimized.
crlf0710
commented
Dec 31, 2017
|
Today is the end of the year. |
This comment has been minimized.
This comment has been minimized.
|
@aturon what is the criterion for reopening this issue to continue discussion? This feature is one of the few ergonomics changes that benefit both new users and existing users without adding much cognitive load. If we want to focus on polishing the language instead of opening other new RFCs this year, this is one of the good candidates. |
contactomorph commentedDec 13, 2015
Rfc for delegation of implementation