I would like to define a square root function, that is generic over all numerical types that can be casted into an f32
. I am aware, that my current approach can suffer precision issues, but I am mainly interested in the concept for how I can create such a function.
Here is my current approach:
fn integer_sqrt<T>(num: T) -> T where T: From<f32>, f32: From<T>{
f32::from(num).sqrt().into()
}
The function itself does compile, but it is hardly of any use, because when I for example try to call the function using an u32
, I get the following compilation errors:
error[E0277]: the trait bound `u32: From<f32>` is not satisfied
--> src/main.rs:2:28
|
2 | let res = integer_sqrt(25u32);
| ------------ ^^^^^ the trait `From<f32>` is not implemented for `u32`
| |
| required by a bound introduced by this call
|
= help: the following implementations were found:
<u32 as From<Ipv4Addr>>
<u32 as From<NonZeroU32>>
<u32 as From<bool>>
<u32 as From<char>>
and 2 others
note: required by a bound in `integer_sqrt`
--> src/main.rs:6:42
|
6 | fn integer_sqrt<T>(num: T) -> T where T: From<f32>, f32: From<T>{
| ^^^^^^^^^ required by this bound in `integer_sqrt`
error[E0277]: the trait bound `f32: From<u32>` is not satisfied
--> src/main.rs:2:15
|
2 | let res = integer_sqrt(25u32);
| ^^^^^^^^^^^^ the trait `From<u32>` is not implemented for `f32`
|
= help: the following implementations were found:
<f32 as From<i16>>
<f32 as From<i8>>
<f32 as From<u16>>
<f32 as From<u8>>
note: required by a bound in `integer_sqrt`
--> src/main.rs:6:58
|
6 | fn integer_sqrt<T>(num: T) -> T where T: From<f32>, f32: From<T>{
| ^^^^^^^ required by this bound in `integer_sqrt`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to 2 previous errors
I do understand why I get these errors, however I do not understand why <f32 as From<u32>>
and <u32 as From<f32>>
are not implemented, when I can easily cast between them using as
.
Here is a Playground illustrating the problem.
Is there any way to achieve the desired behaviour without using the num-traits
crate?
CodePudding user response:
I usually solve this with macro and custom trait:
pub trait IntSqrt {
fn isqrt(self) -> Self;
}
macro_rules! impl_int_sqrt {
($($t:ty)*) => {
$(
impl IntSqrt for $t {
#[inline]
fn isqrt(self) -> Self {
(self as f64).sqrt() as Self
}
}
)*
};
}
impl_int_sqrt!(i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize);
#[cfg(test)]
#[test]
fn test() {
assert!(1i8.isqrt() == 1);
assert!(2i16.isqrt() == 1);
assert!(3u32.isqrt() == 1);
assert!(4usize.isqrt() == 2);
assert!(5.isqrt() == 2);
assert!(6.isqrt() == 2);
assert!(7.isqrt() == 2);
assert!(8.isqrt() == 2);
assert!(9.isqrt() == 3);
}
CodePudding user response:
There is an open RFC for that: RFC 2484: FromLossy
and TryFromLossy
trait.
In the meantime, you can use num-traits
's AsPrimitive
trait:
fn integer_sqrt<T>(num: T) -> T
where
T: AsPrimitive<f32>,
f32: AsPrimitive<T>,
{
num.as_().sqrt().as_()
}