-
Notifications
You must be signed in to change notification settings - Fork 12.5k
-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to automatically derive traits on newtypes #8353
Comments
So as pointed out by folks on IRC, the 3rd attempt is indeed workable and I am an idiot, this makes it run fine: use std::rand::{IsaacRng, XorShiftRng, RngUtil, Rng};
fn main() {
let mut rng = rng();
printfln!(rng.gen_bytes(10));
}
struct StrongRng(IsaacRng);
impl Rng for StrongRng {
pub fn next(&mut self) -> u32 {
return (**self).next();
}
}
pub fn rng() -> StrongRng {
StrongRng(IsaacRng::new())
}
struct WeakRng(XorShiftRng);
impl Rng for WeakRng {
pub fn next(&mut self) -> u32 {
return (**self).next();
}
}
pub fn weak_rng() -> WeakRng {
WeakRng(XorShiftRng::new())
} Still the proposed solution is shorter and nicer with less boilerplate. |
I imagine a more generic syntaxext, for encoding "Proxy Objects". For example: #[Proxy(Rng, (*self))]
struct WeakRng(XorShiftRng); would expand to struct WeakRng(XorShiftRng);
impl Rng for WeakRng {
pub fn next(&mut self) -> u32 {
(*self).next()
}
} This would also allow: #[Proxy(SomeTrait, self.a)]
struct Foo {
a: SomeT,
b: OtherT
} The form is #[Proxy(TraitName, Expression)], and it implements every method on TraitName as |
The problem with that is it breaks down if you have The idea in the OP is GHC's GeneralizedNewtypeDeriving (bunch of detail there, might be worth reading), which I support. One notable property of it is that it re-uses the same |
Plausible. Though you know that all the cool kids would call this (ProxyDelegateFactoryDecoratorOneMoreLevelOfIndirection joke goes here) |
Delegate, proxy, whatever. I'm not jiggy with the jive yet. @pcwalton expressed concern that GeneralizedNewtypeDeriving broke GHC's type system, and that it still hasn't been fixed. I don't know anything about such things, I just know that text-based code generation similar to the attribute I suggest works for a lot of things 😄 |
In any case the compiler could probably do inlining and other stuff to make the cost go away, those "proxy" or "delegate" methods are awful small |
Here's the paper discussing the problem and GHC's solution to it, if anyone's interested. The basic shape of it is that GNTD acts as if, for the purposes of deriving those traits, the newtype and its base were the same type. And, representationally, they are. But otherwise they aren't, and if you have ways to distinguish them and "branch" on the difference at the type level - GADTs, type-level functions (~associated types), what have you - you can derive a contradiction. I don't know if Rust has any way to do that right now (those examples are things it doesn't have), but it's something to keep in mind. If GNTD were restricted to only deriving impls you could also write by hand, that would remove the potential for conflict, so if we don't want to add a similar distinction to the type system between equality of types and equality of representations as GHC will soon have, that might be the way to go. The broader issue the paper discusses is that while you can cast between a newtype and its base at no cost, that's no longer true if they're embedded in another type. E.g. if you have |
This cannot work as a syntax extension: it has no idea what methods a given trait has, nor their signature. It might work if we extend it to something like: #[deriving(Rng(method="fn next(&mut self) -> u32"))]
... so the syntax extension had access to the signatures. In any case, one doesn't need to implement Also, I proposed something very similar to this in #7773. (edit: Whoops, already linked to.) |
@huonw attributes can be handled at any point in the pipeline, what is the problem with doing this during/after typecheck? |
@cmr, none at all, but it means it can't be a normal In any case the "Returning traits as a makeshift interface" is essentially a trait object: |
I think the Decorator approach is fine here, at least for 1.0. Yes, it involves boilerplate, which is a (small) shame. But I don't want people distracted by this bug in the short term. We used to have a "Far future" milestone to nominate and/or tag such items, but I guess it is gone now. So, what, do I explicitly nominate this for "P-low" ? I don't know if that conveys my intent correctly. |
This RFC seems related: rust-lang/rfcs#186 |
This issue has been moved to the RFCs repo: rust-lang/rfcs#479 |
Disclaimer
Please bear with me, this contains long code samples but you can skip through most of them until the last one if you are only mildly interested, they're just there to illustrate my point.
Problem
While adding comments on
rand::rng()
I figured that it would be a lot better if the function would return a generic "interface" type for Rngs instead of returning the type of the particular struct it uses at the moment. However after discussing this with @kballard and @cmr on IRC, it seems that it is now pretty much impossible to achieve.I'll go through the variants I researched, and then propose a solution (which may not be realistic, I have no clue about rustc internals, but I hope it is).
Failed attempt 1: Returning traits as a makeshift interface
This failed miserably because it's just not how the language works at all, and I got that now, but it's included for completeness.
Failed attempt 2: Using newtypes to "hide" the real implementation
This would be an ok workaround, it's not perfect but it seems to be workable given the constraints of the language and isn't too crazy, however it fails because it is missing the proper implementations:
Failed attempt 3: Implementing a decorator newtype
This probably is workable unless I don't get how to work around the compiler errors that are left, but I assume it's not a dead end. The problem though is that it is extremely verbose and seriously painful to write all this boilerplate just to have a straight decorator wrapping the underlying struct.
Proposal
The ideal way would be to allow the newtype to derive traits, so that making a decorator is not a PITA anymore. As you see right now it fails because the derive can't find the right impl, but I think it should just create it on the fly instead of complaining. That way we get explicit "interface" definition by defining which traits are implemented on the newtype, and we can return that and avoid a BC fiasco when we need to change the underlying RNG (see #8349 for more info on that).
I'd be happy to try and help implement this feature because I strongly believe this is needed, but to be honest I have no clue where to even start so pointers would be nice if the consensus is that it is a good thing and that it is at all feasible :)
The text was updated successfully, but these errors were encountered: