Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a generic function bounded to integer types

In my attempt to learn rust I am starting with some basic exercises. I have written a simple function in what I hope is idiomatic rust to count the number of set bits in an integer.

fn bit_count(x: u32) -> u32 {
    (0..32).into_iter().map(|i| (x >> i) & 1).sum()
}

fn main() {
    println!("{} has {} set bits.", 5, bit_count(5));
}

Now I want to make the function generic so that I can pass any integer type: i32, u32, i64, u64... etc.

I am quite familiar with tmp in c++ but my attempt with rust generics have failed, so far I have this:

extern crate num;

fn bit_count<T>(x: T) -> T
where
    T: num::Integer + std::ops::BitAnd + std::ops::Shr + num::NumCast,
    std::ops::Range<T>: std::iter::IntoIterator,
{
    (T::zero()..num::NumCast::from(32).unwrap())
        .into_iter()
        .map(|i| (x >> num::NumCast::from(i)) & T::one())
        .sum()
}

fn main() {
    println!("{} has {} set bits.", 5, bit_count(5));
}

I saw the num crate advertised and it seemed like a good fit. I was expecting to have T: num::Integer and be done, however I feel like I'm spiraling down a rabbit hole here and I can't seem to get the right combination of bounds.

Any suggestions would be great! and any tips to make my code more idiomatic would also be helpful, thanks.

like image 247
nitronoid Avatar asked Nov 08 '19 22:11

nitronoid


People also ask

What are bounded-types in generics in Java?

These are known as bounded-types in generics in Java. You can declare a bound parameter just by extending the required class with the type-parameter, within the angular braces as − In the following Java example the generic class Sample restricts the type parameter to the sub classes of the Number classes using the bounded parameter.

What is a bounded type in C++?

The concept of limiting type parameter upto certain type is known as bounded types. In this, extends keyword is used to bound the type parameter. It simply tells the compiler to allow only parent class or its subclasses types that T has extended, meaning,it restricts in passing types.

What is generics in Java?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc, and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types. An entity such as class, interface, or method that operates on a parameterized type is called ...

Does Java generics support multiple bounds?

Java Generics supports multiple bounds also, i.e., In this case, A can be an interface or class. If A is class, then B and C should be interfaces. We can’t have more than one class in multiple bounds. This article is contributed by Saket Kumar.


1 Answers

Got there in the end. Turns out I needed to use the num::PrimInt trait as my bound because it includes all of the bitwise operations and casts. The num::Integer is less constrained and models an integer in the pure mathematical sense, so no bitwise operations.

The final code I have looks like this:

extern crate num;

fn bit_count<T>(x: T) -> T
where
    T: num::PrimInt + std::iter::Sum,
{    
    let n_bits = std::mem::size_of::<T>() * u8::BITS as usize;
    (0..n_bits).into_iter().map(|i| (x >> i) & T::one()).sum()

}

fn main() {
    println!("{} has {} set bits.", 5, bit_count(5u32));
    println!("{} has {} set bits.", 5, bit_count(5i32));
    println!("{} has {} set bits.", 5, bit_count(5i64));
}

It would be nice to not need that T::one() but it seems there's no way around it. Additionally the std::iter::Sum trait was needed in my bounds to allow the functional workflow.

The num crate actually has a function to count the number of set bits too num::PrimInt::count_ones.

like image 150
nitronoid Avatar answered Oct 10 '22 18:10

nitronoid