0

I wrote a length function for u32. I could easily copy/paste/edit to other int types, but when I try to use generics I'm getting stuck. Are there concepts I'm misunderstanding?

The passing length function

fn len_int(n: u32) -> u32 { // 0
    std::iter::repeat_with({
        let mut l = 0;
        // can't call pow on ambiguous numeric type
        move || match n / 10u32.pow(l) { // 1
            0 => 0,
            _ => {
                l += 1;
                1
            }
        }
    })
        .take_while(|&x| x != 0)
        // count returns usize
        .count() as u32 // 2
}

A failing generic length function: I

fn len_int<T>(n: T) -> T
where
    T: Copy + Clone,
{
    std::iter::repeat_with({
        let mut l = 0;
        move || match n / 10.pow(l) {
            //1
            0 => 0,
            _ => {
                l += 1;
                1
            }
        }
    })
    .take_while(|&x| x != 0)
    .count() at T // 2
}

The compiler tells me I can't call method pow on ambiguous numeric type {integer} cannot divide T by type error Or convert count() at the end with an as T, since T is not a primitive type. playground

cryptograthor
  • 455
  • 1
  • 7
  • 19
  • How/where is it failing? Include the error messages and details. – Herohtar Mar 15 '20 at 22:46
  • The provided playground link leads to completely different code… – Jmb Mar 16 '20 at 07:31
  • 1
    @jmb, Sorry that seems to happen sometimes, forget to check it. it works now. – cryptograthor Mar 16 '20 at 11:09
  • 1
    Please see https://stackoverflow.com/questions/26810793/how-can-i-create-an-is-prime-function-that-is-generic-over-various-integer-types for dealing with generic integer types. Also, some exponent math tells me that no matter what integer you pass in, you'll never overflow `usize` which is what `.count()` returns. Therefore, you really should just return `usize`. – Optimistic Peach Mar 16 '20 at 19:30
  • Also, the simplest method of calculating the number of digits a number occupies in a given base (10 in this case), is to that the log of that number in said base. In this case, log base ten of x. Then, taking the ceiling of that makes it a whole number. Example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=761d3598c016dea5c31ae272f69ef33d – Optimistic Peach Mar 16 '20 at 19:33
  • @OptimisticPeach except that this may give wrong results for large numbers that loose precision when converted to a `f64`. The simplest exact method is to repeatedly divide by 10 ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c0cd4f07339b3d419ad8b912aefd6349)). – Jmb Mar 17 '20 at 07:38
  • @Jmb, yes, but unless we're dealing with numbers greater than 2^53, we should be fine. Then again, it's all up to OP. – Optimistic Peach Mar 17 '20 at 15:47
  • See this possible [solution](https://stackoverflow.com/questions/69297477/getting-the-length-of-an-int/76442105#76442105) – Claudio Fsr Jun 09 '23 at 16:34

2 Answers2

0

You may have specific types in your mind, but the compiler doesn't know that. To the compiler a generic type T can be totally anything. It can be a string. It can be a reference to a function pointer. It can be a vector of file handles. Anything. So it will require your code to work with all of these possible types at the same time.

To work with more specific types, you must declare ahead of time everything you want the type to support. The compiler won't let you use any operation you haven't declared.

There's no trait for .pow in the Rust's standard library, so you can't declare that out of the box. You can make one yourself, or use num-traits.

Kornel
  • 97,764
  • 37
  • 219
  • 309
0

We can define a trait and then implement the trait for the concrete types you want to support in your generic function:

pub trait IntLenExt<T> {
    fn is_divis(&self, power: u32) -> bool;
    fn cast_count(count: usize) -> T;
}

impl IntLenExt<u32> for u32 {
    fn is_divis(&self, power: u32) -> bool {
        self / 10u32.pow(power) == 0
    }

    fn cast_count(count: usize) -> u32 {
        count as u32
    }
}

impl IntLenExt<u64> for u64 {
    fn is_divis(&self, power: u32) -> bool {
        self / 10u64.pow(power) == 0
    }

    fn cast_count(count: usize) -> u64 {
        count as u64
    }
}

fn len_int<T>(n: T) -> T
where
    T: Copy + Clone + IntLenExt<T>,
{
    T::cast_count(std::iter::repeat_with({
        let mut l = 0;
        move || match n.is_divis(l) {
            //1
            true => 0,
            false => {
                l += 1;
                1
            }
        }
    })
    .take_while(|&x| x != 0)
    .count()) // 2
}


fn main() {
    let input: u64 = 100;
    println!("len_int({}) => {}", input, len_int(input));
}
jakedipity
  • 890
  • 8
  • 19