-
Notifications
You must be signed in to change notification settings - Fork 26
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
Derive various num traits for newtypes #17
Conversation
Pushed a fix for rust 1.15 compatibility. |
079f045
to
b64a25e
Compare
I just noticed #1. I'm not sure if anything came of the more general |
fd55292
to
a69f860
Compare
Added some docs. Build failure looks spurious. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I restarted that failed build -- ✔️
This looks really good! My only concern is how to get proper support for 128-bit integers.
@@ -10,6 +10,7 @@ | |||
|
|||
#![crate_type = "proc-macro"] | |||
#![doc(html_root_url = "https://docs.rs/num-derive/0.2")] | |||
#![recursion_limit="512"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... It may not need to be this high - I only know that 256 is not enough.
None | ||
} | ||
} | ||
_ => None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if this could also work with a single Fields::Named
, but we can leave that as a future extension. I guess we'd have to return an extra token to abstract how to access .0
vs. .inner
, and find some way to abstract the newtype constructor too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a specialised error message if the user tries to derive for a named-field-newtype. This is a lazy cop-out on my part, but I think it's better than nothing.
--> tests/newtype.rs:89:5
|
89 | Float,
| ^^^^^
|
= help: message: num-derive doesn't know how to handle newtypes with named fields yet. Please use a tuple-style newtype, or submit a PR!
src/lib.rs
Outdated
fn from_i32(n: i32) -> Option<Self> { | ||
<#inner_ty as _num_traits::FromPrimitive>::from_i32(n).map(#name) | ||
} | ||
#[cfg(has_i128)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this cfg
actually work? I'm assuming that this is not interpreted locally, just quoted and emitted as-is to the derived code.
This is how the methods are defined in num-traits
, but that has a build script to set the cfg
, which only applies within that crate. So I think this derive will be missing out, unless the target crate also happens to set has_i128
.
I think what we could do is add a similar build script to num-derive
, and check #[cfg(has_i128)]
locally (outside of quote!
) to determine whether we should emit those methods in the derived output.
(The enum derivation doesn't worry about this yet, since repr128
is still unstable.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see; you're saying that, in the end, these functions are unconditionally ignored. I'll implement your suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done; I confirmed that the functions are present in the expansion under 1.29, and it still builds with 1.15.
src/lib.rs
Outdated
}; | ||
|
||
res.into() | ||
dummy_const_trick("FromPrimative", &name, impl_).into() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spelling: FromPrimitive
(though I know this is hidden anyway)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, fixed.
This equates to deriving Add, Sub, Mul, Div, and Rem, where RHS=Self and Output=Self.
This is needed for older compilers, even though we know they won't have i128 support anyway...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! I just added explicit tests for the 128-bit methods.
bors r+ |
17: Derive various num traits for newtypes r=cuviper a=asayers This PR adds deriving macros for the following traits: * FromPrimitive * ToPrimitive * NumOps<Self, Self> * NumCast * Zero * One * Num * Float These macros only work when applied to a newtype where the inner type already implements the trait in question. They produce the obvious "unwrap-call-rewrap" implementation. The `FromPrimative` and `ToPrimative` macros now handle both newtypes and the enums that they used to handle. The odd one out is `NumOps`, for two reasons. First, `NumOps` is just a trait alias for `Add + Sub + Mul + Div + Rem`, so those are the traits which the macro generates impls for. I suppose it's not a great user experience to say `#[derive(Foo)]` and end up with `impl Bar`, but I think in this case it's just about OK. After all, the user needs only to look at the definition of `NumOps` to know that this is only possible behaviour - there's no other way to get an impl for `NumOps`. The other reason this one is strange is that when the user says `#[derive(NumOps)]`, the macro decides that what they want is an impl for `NumOps<Rhs=Self, Output=Self>`. I think this makes sense though; these are the default type parameters, so when you say `NumOps` unqualified this is what it means. As you can probably tell, my objective here was to get to `Float`. That's why this PR only provides macros for a subset of the traits in `num_traits`. Our codebase has a bunch of newtyped `f64`s, and I don't want people unwrapping them all the time to work with them. ### To-do - [x] docs - [x] tests - [x] RELEASES entry Co-authored-by: Alex Sayers <[email protected]> Co-authored-by: Josh Stone <[email protected]>
Build succeeded |
Thanks @cuviper! |
This PR adds deriving macros for the following traits:
These macros only work when applied to a newtype where the inner type already
implements the trait in question. They produce the obvious
"unwrap-call-rewrap" implementation.
The
FromPrimative
andToPrimative
macros now handle both newtypes and theenums that they used to handle.
The odd one out is
NumOps
, for two reasons. First,NumOps
is just a traitalias for
Add + Sub + Mul + Div + Rem
, so those are the traits which themacro generates impls for. I suppose it's not a great user experience to say
#[derive(Foo)]
and end up withimpl Bar
, but I think in this case it's justabout OK. After all, the user needs only to look at the definition of
NumOps
to know that this is only possible behaviour - there's no other way to get an
impl for
NumOps
.The other reason this one is strange is that when the user says
#[derive(NumOps)]
, the macro decides that what they want is an impl forNumOps<Rhs=Self, Output=Self>
. I think this makes sense though; these arethe default type parameters, so when you say
NumOps
unqualified this is whatit means.
As you can probably tell, my objective here was to get to
Float
. That's whythis PR only provides macros for a subset of the traits in
num_traits
. Ourcodebase has a bunch of newtyped
f64
s, and I don't want people unwrappingthem all the time to work with them.
To-do