是否可以在不使用 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);
}
我想定义一个平方根函数,它对所有可以转换为 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);
}