Fast generic conversion from usize to u32

How does one create a version of my_fun below with prototype

fn my_generic<U>(index : usize) -> U

The index keeps increasing for a huge number of conversions and validity
of the conversions should be checked at the end (not for each conversion).

fn my_fn(index : usize) -> u32 {  
   index as u32 
}

fn my_generic< U : From<usize> >(index : usize) -> U {  
   index.into()
}

fn main()
{  let index         : usize = 5;
   let index_fn      : u32   = my_fn(index);
   let index_generic : usize = my_generic(index);
   println!( "index_fn = {index_fn}" );
   println!( "index_generic = {index_generic}" );
   //
   // If you uncomment the statement below you get the error
   // the trait From<usize> is not implemented for u32.
   // let index_generic : u32 = my_generic(index);
}
/* 
// The following function results in the compiler error below it.
fn my_generic<U>(index : usize) -> U {  
   index as U 
}
an as expression can only be used to convert between primitive types ..
*/

I'm not sure what your setup is, but maybe you can do the conversion at the very end of your algorithm and do all the index computations solely with usize, before trying to convert the index to u32?

As to why there is no From<usize> for u32 implementation, it is because the conversion can fail with an overflow on 64-bit machines. If you can change the signature of my_generic you could use TryFrom:

use std::convert::TryFrom;

fn my_fn(index : usize) -> u32 {  
   index as u32 
}

fn my_generic< U : TryFrom<usize, Error=E>, E>(index : usize) -> Result<U, E> {  
   index.try_into()
}

fn main()
{  let index         : usize = 5;
   let index_fn      : u32   = my_fn(index);
   let index_generic : usize = my_generic(index).unwrap();
   println!( "index_fn = {index_fn}" );
   println!( "index_generic = {index_generic}" );
   //
   // If you uncomment the statement below you get the error
   // the trait From<usize> is not implemented for u32.
   let index_generic : u32 = my_generic(index).unwrap();
   println!( "index_generic = {index_generic}" );
}

Playground.

However, that would still do the overflow check during the conversion.

Alternatively, you could make my_generic a macro and use as:

macro_rules! my_generic {
    ($x:ident) => { $x as _ };
}

let index: usize = 5;
let index_generic: u32 = my_generic!(index);

I don't understand this requirement. my_fn doesn't do error checking at all, instead it just truncates the value to 32 bits.

I have a tape that stores variable information. If the tape uses u32, I want it to fail at the end when I check the usize that has the value for the next variable. I do not want to check every conversion because this would slow down the taping.

I know how to do this using macros, I am just checking to see if it is possible using generic functions.

I think that try_into is slower that as ?
Since the index for a tape keeps increasing, I can do one check when the tape is done recording and do not need to check every value to see that it is valid.

You can define a trait and then use it in generic functions:

trait WrappingFrom<T> {
    fn wrapping_from(x: T) -> Self;
}

impl WrappingFrom<usize> for u32 {
    fn wrapping_from(x: usize) -> u32 {
        x as u32
    }
}
2 Likes

This makes sense if there is no standard trait for this purpose ?

If you're only using this for usize -> u32 then it doesn't really make sense to use a generic trait. If you need to use it in a generic function with various types then it does make sense.

Here is what works for me:

trait GenericAs<F> {
    fn gas(x: F) -> Self;
}
macro_rules! generic_as{ ($F:ident , $T:ident) => {
   impl GenericAs<$F> for $T {
      fn gas(f : $F) -> $T {
         f as $T
      }
   }
} }
generic_as!(usize, u16);
generic_as!(usize, u32);
// ...
//
fn use_gas<T : GenericAs<usize> > (f : usize) -> T {
    GenericAs::gas(f)
}
fn main()
{  let index         : usize = 5;
   let index_generic : u32   = use_gas(index);
   println!( "index_generic = {index_generic}" );
}

After using GenericAs in my project, I found the following form more natural:

pub trait GenericAs<T> {
    fn gas(self : Self) -> T;
}
//
/// Use as to convert from $F to $T
macro_rules! generic_as{ ($F:ident, $T:ident) => {
    #[doc = concat!( 
        " use as to convert from ", stringify!($F) , " to ", stringify!($T) 
    ) ]
    impl GenericAs<$T> for $F {
        fn gas(self : Self) -> $T {
            self as $T
        }
    }
} }
//
generic_as!(usize, u32);
generic_as!(u32, usize);
// ...
//
fn use_gas<T> (f : usize) -> T where usize : GenericAs<T> {
   GenericAs::gas(f)
}
//
fn main()
{  let index         : usize = 5;
   let index_generic : u32   = use_gas(index);
   println!( "index_generic = {index_generic}" );
}

I'm tryin': `num::WrappingFrom` trait for conversions between integers by scottmcm · Pull Request #3703 · rust-lang/rfcs · GitHub

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.