Skip to content
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

Merged
merged 11 commits into from
Oct 3, 2018
Merged

Conversation

asayers
Copy link
Contributor

@asayers asayers commented Sep 29, 2018

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 f64s, and I don't want people unwrapping
them all the time to work with them.

To-do

  • docs
  • tests
  • RELEASES entry

@asayers
Copy link
Contributor Author

asayers commented Sep 29, 2018

Pushed a fix for rust 1.15 compatibility.

@asayers asayers force-pushed the newtypes branch 2 times, most recently from 079f045 to b64a25e Compare September 29, 2018 17:05
@asayers
Copy link
Contributor Author

asayers commented Sep 30, 2018

I just noticed #1. I'm not sure if anything came of the more general derive_ops stuff discussed there - I couldn't find it.

@asayers asayers force-pushed the newtypes branch 2 times, most recently from fd55292 to a69f860 Compare September 30, 2018 04:54
@asayers
Copy link
Contributor Author

asayers commented Sep 30, 2018

Added some docs. Build failure looks spurious.

Copy link
Member

@cuviper cuviper left a 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"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😅

Copy link
Contributor Author

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,
Copy link
Member

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.

Copy link
Contributor Author

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)]
Copy link
Member

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.)

Copy link
Contributor Author

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.

Copy link
Contributor Author

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()
Copy link
Member

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed.

This is needed for older compilers, even though we know they won't have
i128 support anyway...
Copy link
Member

@cuviper cuviper left a 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.

@cuviper
Copy link
Member

cuviper commented Oct 3, 2018

bors r+

bors bot added a commit that referenced this pull request Oct 3, 2018
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]>
@bors
Copy link
Contributor

bors bot commented Oct 3, 2018

Build succeeded

@bors bors bot merged commit 706ea88 into rust-num:master Oct 3, 2018
@asayers
Copy link
Contributor Author

asayers commented Oct 3, 2018

Thanks @cuviper!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants