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.
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.
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.
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 ...
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With