【问题标题】:How do I make a generic absolute value function?如何制作通用绝对值函数?
【发布时间】:2019-02-14 20:13:35
【问题描述】:

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

【问题讨论】:

    标签: generics rust traits


    【解决方案1】:

    我一般如何确定最小值?基本上相当于 C++ 的 Rust std::numeric_limits&lt;T&gt;::min()?

    您想要 num-traitsnum crates 中的 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)
        }
    }
    

    这会将函数的“内容”委托给完全符合您要求的现有代码,因此您犯错的空间更小。

    【讨论】:

    • 解决了我的两个问题,谢谢。我同意重新发明轮子通常是一个坏主意,但我发现它对学习东西很有用。毕竟我本来可以做 `-128i8.abs() 得到一个错误
    • 我建议返回一个Option&lt;T&gt;,因为这里没有真正的错误,而是一个有效的计算。类似于checked_add
    • @hellow 是的,我同意。但我已经回答了 OP 要求的签名。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多