如何制作通用的绝对值函数?

时间:2019-02-14 20:13:35

标签: generics rust traits

我正在尝试编写一个通用函数,该函数计算任何有符号整数类型的绝对值。当该值是可能的最低负值时(例如,无法表示8位abs(-128)),它应该返回错误。

我为i8工作:

pub fn abs(x: i8) -> Result<i8, String> {
    match x {
        x if x == -128i8 => Err("Overflow".to_string()),
        // I know could just use x.abs() now but this illustrates a problem in the generic version below...
        x if x < 0i8 => Ok(-x),
        _ => Ok(x),
    }
}

fn main() {
    println!("{:?}", abs(-127i8));
    println!("{:?}", abs(-128i8));
}

我无法使用通用版本。具体来说,我有两个问题:

  • 如何一般确定最小值?什么是C ++ std::numeric_limits<T>::min()的Rust等效项?有例如std::i32::MIN,但我不能写std::T::MIN
  • 我在匹配臂上的通用实现错误,带有“无法通过行进方式绑定到模式保护程序中”的负值(但非通用版本没有)。
use num::{traits::Zero, Integer, Signed}; // 0.2.0

pub fn abs<T>(x: T) -> Result<T, String>
where
    T: Signed + Integer + Zero,
{
    match x {
        //x if x == ***rust equivalent of std::numeric_limits<T>::min()** => Err("Overflow".to_string()),
        x if x < T::zero() => Ok(-x),
        _ => Ok(x),
    }
}

fn main() {
    println!("{:?}", abs(-127i8));
    println!("{:?}", abs(-128i8));
}
error[E0008]: cannot bind by-move into a pattern guard
 --> src/main.rs:9:9
  |
9 |         x if x < T::zero() => Ok(-x),
  |         ^ moves value into pattern guard

1 个答案:

答案 0 :(得分:2)

  

如何一般确定最小值?基本上与C ++ std::numeric_limits<T>::min()的Rust等效吗?

您希望从num-traitsnum板条箱中获取Bounded trait,这为您提供了min_value方法:

pub fn abs<T>(x: T) -> Result<T, String>
where
    T: Signed + Integer + Zero + Neg + Bounded + Copy,
{
    match x {
        x if x == T::min_value() => Err("Overflow".to_string()),
        x if x < T::zero() => Ok(-x),
        _ => Ok(x),
    }
}
  

我在匹配臂上的通用实现错误,带有“无法通过行进方式绑定到模式保护程序中”的负值(但非通用版本没有)。

我添加了Copy边界,以避免在模式保护程序中移动值的问题。大多数数字类型应为Copy

也许更好的方法是使用“选中的”运算符变体,例如CheckedSub

pub fn abs<T>(x: T) -> Result<T, String>
where
    T: Signed + Integer + Zero + Neg + CheckedSub,
{
    if x < T::zero() {
        T::zero()
            .checked_sub(&x)
            .ok_or_else(|| String::from("Overflow"))
    } else {
        Ok(x)
    }
}

这将函数的“作用”委托给了完全符合您想要的功能的现有代码,因此您的错误余地更少了。