Integer Constructor Functions

This is an incredibly ridiculous request on its face but please power through it with me and get to my rationalization:

I would like the number fundamentals (u*, i*, f*) to have const fn new(value: Self) -> Self { value } inherent functions.

Close the tab now, if you want. It's incredibly silly on its face. But now let's get to why I want this:

I am the author of bitvec, which makes extensive use of integer and integer-like types, including constructing them from raw bit sequences. bitvec constructs uN unsigned integer fundamentals, Cell<uN> wrappers, and AtomicUN variants.

Cell and AtomicUN both have ::new functions.

uN does not.

This means that, in order to unify the syntax for creating "some integer-like type", I have to use mem::transmute in order to turn a raw integer into the final type. I would vastly prefer to instead generate <$final>::new(produced_value) in my macros, and let the invocation context sort it out.

TL:DR; macros that generalize over different flavors of integer have no unified syntax for creating the final integer type; a ::new function on the numeric primitives would greatly simplify this and remove some uncomfortable corners of source generation in libraries that do this kind of work.


I assume this is light enough that it does not need an RFC. This is an extremely straightforward addition to the primitive APIs, so I plan on doing it myself anyway. I just don't know what the proper form for submitting it for consideration is, so I wanted to write here first rather than just showing up to GitHub with an unasked-for PR and no justification.

Cell<u32> and AtomicU32 and u32 all implement From<u32>. Would it work for your generated code to use <$final>::from(produced_value) to construct all three types?

6 Likes

This sounds, to me, like not all possible alternatives hav been explored. A problem that can be solved with the propped new functions does definitely not need to use transmute without those functions.

Have you considered creating a new trait and using <$final as TheTrait>::trait_method(produced_value)?

3 Likes

a trait wouldn't work because the function needs to be const which is not possible in rust (as far as I know)

how ever if its possible in nightly I assume that would be fine-ish? since const_transmute is nightly only

2 Likes

This doesn't seem inherently ridiculous.

Personally, I'd love to have more traits that unify numeric types. We do need const traits eventually.

Suppose we had const traits available in the language. Would it then suffice to have a trait for this?

7 Likes

depending on how the macro works it might be possible if you are willing to sacrifice some (a lot of) ergonomics:

macro_rules! new {
    (Cell<u32>, $expr:expr) => { ::core::cell::Cell::<u32>::new($expr) };
    (AtomicU32, $expr:expr) => { ::std::sync::atomic::AtomicU32::new($expr) };
    (u32, $expr:expr) => { ::core::convert::identity::<u32>($expr) };
    // ...
}

macro_rules! expected_type {
    ($_:ty) => {};
}

macro_rules! zero {
    // this needs to be `tt*` and not `ty` or we can't match against it in `new!`
    ( $($T:tt)* ) => {{
        // slighly more helpful message if we dont pass in a type
        expected_type! { $($T)* }
        
        // unified syntax!
        new! { $($T)*, 0 }
    }};
}

const fn foo() {    
    let _ = zero! { Cell<u32> };
    let _ = zero! { AtomicU32 };
    let _ = zero! { u32 };
    
    // let _ = zero!( "Quux" );

    // let _ = zero!
}

this would need that all macros that take in a type would need to change from ty to tt and paths wouldn't work, but its something to keep in mind.

I think that this is the macro in question

Doesn't necessarily looks like it requires to work for const contexts.

You don't need a trait for known types.

(I'm not saying there shouldn't be a better way, just showing another workaround.)

6 Likes

Transmutes in const fn were recently stabilized (it hasn't yet hit stable). If there's anything that was blocked on only that, send a PR — I didn't bother to check when I submitted the transmute stabilization.

I'd love to have more traits that unify numeric types. … Would const traits suffice for this?

I also maintain funty, which exists solely to unify numeric types through traits. If I could have const in traits, I would do so immediately and never work with direct types again.


__make_elem is the root case, yes, and I do require it to work in const contexts. mem::transmute has been const fn since stable 1.46, according to rustdoc. That matches my memory of about when I allowed the shared-mut types to be used as type parameters.


You could dispatch from type name to generator function

You're right, I could! I already have the infrastructure in place to dispatch by type name. I honestly completely forgot about convert::identity. I can try threading functions through the macro stack and see how that goes for me. But it'd still be easier if it was just <$type>::new :stuck_out_tongue:


Wrapper workaround

Oooh that's a very good idea. I will actually do that, thanks!

I use this approach a bunch, and it has the downside that it doesn't allow you to pass integer literals of uninferred type when there's impls for multiple integer types .

Edit: Actually, this is not the same thing that I do, but it might still have those type inference issues.

But in Rust 1.46.0 it was only made usable in const items, not const fns (that should have been documented in the docs for the function).

Release notes:

1 Like