我如何创建一个通用函数,它接受任何有符号或无符号整数并将其转换为 u8?

How can I create a generic function that takes any signed or unsigned integer and converts it to a u8?

我想出了一个函数,它应该接受任何有符号或无符号整数并将其映射到 u8。每个负值都变为 0,而每个高于 255 的值都应为 255。我知道这很简单,我的目标只是了解泛型的工作原理。

use std::convert::TryInto;

fn as_byte<T>(source: T) -> u8
where
    T: TryInto<u8>,
    T: PartialOrd<u8>,
{
    if source < 0 {
        return 0;
    }
    if source > 255 {
        return 255;
    }

    source.try_into().unwrap_or(0)
}

我认为将我的泛型限制为我实际想用 source 做的事情是有意义的,所以我声明我希望能够将它与 u8 进行比较并且能够转换为 u8(如果我之前捕获到超出范围的值,应该 不会失败)。

如果我调用 as_byte(1234i32),它将失败,因为 i32 没有实现 PartialOrd<u8>。即使有实施,这也会失败,因为 source 在第一次比较时被移动,然后在第二次检查中移动后被使用。

如果我像 as_byte(&1234i32) 那样通过引用传递 source,情况会变得更糟。现在 TryInto<u8> 特征也没有实现,因为 u8.

没有 From<&i32>

我明白为什么会出现这些问题,但我不知道如何解决它们。我尝试将约束更改为 where &'a T 或将参数更改为 source: &T,或同时更改两者。这给我留下了类似的问题,其中引用类型的特征没有实现。这让我觉得我一开始就没有正确理解 borrowing/references 的概念。

我的思路错在哪里?

  1. 如果PartialOrd<u8> for i32存在,这个方法的声明应该是什么样的?

  2. 知道PartialOrd<u8> for i32不存在,还要怎么约束类型参数呢? 是相关的并且在某种程度上回答了我的问题 - 但我可以(并且可能应该)不实现 primitive/std 类型的特征,我可以吗?

你可以这样写as_byte()

fn as_byte<T>(source: T) -> u8
where
    T: TryInto<u8> + From<u8> + PartialOrd,
{
    if source < 0.into() {
        return 0;
    }
    if source > 255.into() {
        return 255;
    }

    source.try_into().unwrap_or_else(|_| unreachable!())
}

Playground

由于u8非常小,所以将0和255转换为T然后比较比反过来比较更有意义。要求 PartialOrd<u8> 使 as_bytes 独立编译,但在使用具体整数类型调用时不会。这是因为Rust的整数类型目前没有实现不同宽度类型的比较,你需要将两个操作数转换为相同的类型。

我不确定为什么 source 移动有问题 - PartialOrd 定义了 &self&rhs 的比较,所以它永远不会导致移动.