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

How do I make a generic absolute value function?

我正在尝试编写一个通用函数来计算任何有符号整数类型的绝对值。当值是可能的最低负值时,它应该 return 一个错误,例如 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));
}

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

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

How do I determine generically the minimum value? Basically the Rust equivalent of the C++ std::numeric_limits<T>::min()?

您想要 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),
    }
}

My generic implementation errors on the match arm for negative values with "cannot bind by-move into a pattern guard" (yet the non-generic version doesn't.)

我添加了一个 Copy 绑定,以避免移动模式守卫中的值的问题。大多数数字类型应该是 Copy.

也许更好的方法是使用 "checked" 运算符变体,例如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)
    }
}

这会将函数的 "meat" 委托给现有代码来完全按照您的要求执行,因此您犯错的空间更小。