是否可以在不使用 num_traits 板条箱的情况下创建通用数学函数?

Is it possible to create a generic mathematical function without using the num_traits crate?

我想定义一个平方根函数,它对所有可以转换为 f32 的数值类型都是通用的。我知道,我目前的方法可能会遇到精度问题,但我主要对如何创建这样一个函数的概念感兴趣。

这是我目前的做法:

fn integer_sqrt<T>(num: T) -> T where T: From<f32>, f32: From<T>{
    f32::from(num).sqrt().into()
}

函数本身可以编译,但几乎没有任何用处,因为当我尝试使用 u32 调用函数时,我得到以下编译错误:

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

我明白为什么会出现这些错误,但是我不明白为什么 <f32 as From<u32>><u32 as From<f32>> 没有实现,而我可以使用 as 在它们之间轻松转换。

这里 Playground 说明了这个问题。

有没有什么方法可以在不使用 num-traits 板条箱的情况下实现所需的行为?

我通常用宏和自定义特征来解决这个问题:

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);
}