paholg / typenum

Compile time numbers in Rust.
https://crates.io/crates/typenum
Other
518 stars 46 forks source link

Minimally necessary Rust unsigned integer types on fields of structs, conditioned on `typenum::consts` as generic type parameters #203

Open jymchng opened 9 months ago

jymchng commented 9 months ago

Context:

I am writing sosecrets-rs.

Let's imagine that I have the following struct:

struct RunTimeSecret<T, MEC: Unsigned + MinimallyRepresentableUInt> {
    inner_secret: T,
    exposure_count: <MEC as MinimallyRepresentableUInt>::Type, // resolve to be `u8`
}

let var = RunTimeSecret::<String, U1> {
    inner_secret: "MySecret".to_string(),
    exposure_count: 256, // it does not compile and it should not compile because `256` > `u8::MAX`.
};

The intent of RunTimeSecret is to 'panic' or return an error only if exposure_count is greater than MEC::USIZE.

Now, I can always make the field exposure_count to be of type usize but that will be a waste because if MEC = U69, then u8 would be all that's needed to represent exposure_count since it is never larger than 69.

Summary:

I am trying to use typenum type-level integers to conditionally type fields of structs.

Attempt:

I wrote the following:

use typenum::{U1, Unsigned};

trait MinimallyRepresentableUInt {
    type Type;
}

impl MinimallyRepresentableUInt for U1 {
    type Type = u8;
}

Sadly, this is definitely not going to scale for all U<*>, i.e. U69, U64905405, etc.

Of course,

trait GiveMeu8AsExample<const N: usize> {
    type Type;
}

impl<const N: usize> GiveMeu8AsExample<N> for () {
    type Type = u8;
}

impl<T: Unsigned> MinimallyRepresentableUInt for T {
    type Type = <() as GiveMeu8AsExample::<{ <T as Unsigned>::USIZE }>>::Type;
}

this doesn't work because const generics expressions are not on stable Rust.

Just curious if there is a way to implement such a trait to all U<*> even if it involves macros and what not.

Playground

paholg commented 9 months ago

I would guess that you could implement MinimallyRepresentableUInt for all U* generically.

I don't really have time to do this right now, but if you look at the implementations of some of the more complex functionality of typenum (such as division or gcd), it may give you some ideas.

Basically, Rust's type system is turing complete, so you can do pretty much anything. But the only tools are essentially recursion and branching, so code gets messy fast.

jymchng commented 9 months ago

I would guess that you could implement MinimallyRepresentableUInt for all U* generically.

I don't really have time to do this right now, but if you look at the implementations of some of the more complex functionality of typenum (such as division or gcd), it may give you some ideas.

Basically, Rust's type system is turing complete, so you can do pretty much anything. But the only tools are essentially recursion and branching, so code gets messy fast.

@paholg

Thank you for your response. It's cool now, I've gotten help from another genius (like yourself :) on the Rust forum.

https://users.rust-lang.org/t/making-a-type-level-type-function-with-typenum-crate/107008/4?