greyblake / nutype

Rust newtype with guarantees 🇺🇦 🦀

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add const getter for Copy types

helgoboss opened this issue · comments

I want to propose optionally adding a const getter method to newtypes that wrap a Copy value.

Motivation

Given the following newtype:

/// Represents a tempo measured in beats per minute.
#[nutype(
    new_unchecked,
    validate(finite, greater_or_equal = 1.0, less_or_equal = 960.0),
    derive(
        Copy,
        Clone,
        Eq,
        PartialEq,
        Ord,
        PartialOrd,
        Debug,
        Default,
        Display,
        FromStr,
        Into,
        TryFrom
    ),
    default = 1.0
)]
pub struct Bpm(f64);

impl Bpm {
    /// The minimum possible value.
    pub const MIN: Self = unsafe { Self::new_unchecked(1.0) };

    /// The maximum possible value.
    pub const MAX: Self = unsafe { Self::new_unchecked(960.0) };
}

(Side note: Being able to call Self::new_unchecked from a const context needs this)

Now I would like to do things like this:

const BPM_SPAN: f64 = Bpm::MAX.into_inner() - Bpm::MIN.into_inner();

Above code is currently not possible because into_inner() is not const:

            pub fn into_inner(self) -> #inner_type {
                self.0
            }

Thoughts

Just making it const in general unfortunately doesn't work. Because it takes self by value (which is the correct choice for a method of this name). And that fails to compile if the wrapped type is heap-allocated ("the destructor for this type cannot be evaluated in constant functions").

I see 2 possibilities.

a) Making into_inner const, but only for Copy types

            pub const fn into_inner(self) -> #inner_type {
                self.0
            }

b) Introducing a separate const get method, but only for Copy types

            pub const fn get(self) -> #inner_type {
                self.0
            }

Personally, I prefer b. There are quite many examples in the standard library where a wrapper exposes its wrapped Copy value in a get method, so it appears to be a good practice. But ultimately I don't mind.

Do you think it's somehow possible to do a or b? Just for Copy types? And if yes, would you consider it? I would be happy to write a PR.

Addition: I have the feeling that it would be hard doing something special just for Copy types. If this is too hard or even impossible, adding the const getter just to integers and floating point wrappers would be the alternative - that would cover most of the use cases I guess.

Here's a prototypical implementation that adds pub const fn get(self) -> #inner_type if the newtype itself derives Copy. What do you think about this approach? I could turn it into a proper PR including tests if you are fine with it.

helgoboss@e186e09

@helgoboss Hi, thanks for your report!

I think it's partially overlaps with #35

The problem with adding new method .get() is that it typically returns a reference, not a value itself. I don't also want to have some extra methods for not real reason. If you need a reference, you could use AsRef trait, but year it won't work with const I guess.

So I'd prefer rather option A, meaning implementing const fn into_inner() for integer and float types.
If you want you can go ahead and open a PR for that. Just please make sure to add proper tests.

Ok, I understand. I hope to open a PR soon.