我对C++很有经验,并开始try 使用Ruust.

在try 实现一些简单的泛型函数时,我遇到了以下问题:

use std::ops::BitAnd;
use std::cmp::Eq;

fn is_odd_i32(x: u32) -> bool {
    if x & 1_u32 == 1_u32 { true } else { false }
}

fn is_odd<T: BitAnd + Eq>(x: &T) -> bool {
    if (*x & (1 as T)) == (1 as T) { true } else { false }
}

fn main() {
    println!("is_odd -> '{}'", is_odd(&23_u64));
    println!("is_odd -> '{}'", is_odd(&23_u32));
}

问题似乎是将按位AND结果与0或1进行比较.我知道要实现这一点,1(或0)必须可以转换为类型T,但不知道如何实现.我也试了T::try_from(1_u8).ok().unwrap()次,但这也不起作用.

我不知道该怎么解决这个问题.

我收到的错误是:

error[E0369]: binary operation `==` cannot be applied to type `<T as BitAnd>::Output`
  --> src/main.rs:27:24
   |
27 |     if (*x & (1 as T)) == (1 as T) { true } else { false }
   |        --------------- ^^ -------- T
   |        |
   |        <T as BitAnd>::Output
   |
help: consider further restricting the associated type
   |
26 | fn is_odd<T: BitAnd + Eq>(x: &T) -> bool where <T as BitAnd>::Output: PartialEq<T> {
   |                                          +++++++++++++++++++++++++++++++++++++++++

error[E0605]: non-primitive cast: `{integer}` as `T`
  --> src/main.rs:27:14
   |
27 |     if (*x & (1 as T)) == (1 as T) { true } else { false }
   |              ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error[E0605]: non-primitive cast: `{integer}` as `T`
  --> src/main.rs:27:27
   |
27 |     if (*x & (1 as T)) == (1 as T) { true } else { false }
   |                           ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

顺便说一句,我只是在玩弄特征和泛型,这不是测试整数是否奇数的最好方法.

推荐答案

代码不能工作的主要原因是Rust的泛型的工作原理与C++的S模板完全不同.

在Rust的泛型中,约束是您唯一可以依赖的东西.所以当你写T: BitAnd + Eq的时候,这就是S T的能力的范围.这里没有说T甚至是数字相邻的,因为您所知道的是,为了方便起见,有人在哈希集上实现了&.因此,您的强制转换对编译器来说完全没有意义(强制转换从一开始就不是泛型操作),因此它会抱怨.

为了使用泛型,您需要一种表示这些操作的泛型方法,类似于将所有基本数值运算定义为泛型的"数字塔",但早在早期,核心团队就认为这对核心语言和标准库没有用处,过go 的少量工作被移到/移到the num ecosystem.为此,具体地说,the One特征:

fn is_odd<T: BitAnd + Eq + One>(x: &T) -> bool {
    *x & T::one() == T::one()
}

(我go 掉了无用的语法部分)

当然,如果你应用它,你会发现很多问题:

  • BitAnd不保证其输出与其输入以任何方式相关,因此您需要将其限制为有意义的内容,一种 Select 是只请求T & T返回T:

    fn is_odd<T: BitAnd<Output=T> + Eq + One>(x: &T) -> bool {
        *x & T::one() == T::one()
    }
    
  • 你不能把东西移出引用,这正是*x试图在这里做的.您可以将x限制为Copy种类型(这允许从引用中复制出来),也可以不将x作为引用,我 Select 了后者,因为我不明白这一点:

    fn is_odd<T: BitAnd<Output=T> + Eq + One>(x: T) -> bool {
        x & T::one() == T::one()
    }
    

现在,如果您修复了调用点以删除引用,它现在可以工作了.is_odd现在可以像您希望的那样灵活地工作(如果一个类型碰巧以一种没有意义的方式实现了所有这三种操作,这可能是numis_odd as a non-defaulted operation of the Integer trait的原因).

顺便说一句,在这种情况下,你的println是dbg的过于复杂的版本:

    dbg!(is_odd(23_u64));

=>;

[src/main.rs:9] is_odd(23_u64) = true

另外,您也可以使用转换版本(不使用num),但在这种情况下,如最初所述,您需要确切地告诉编译器您需要什么转换,特别是T需要可以从U8转换,也就是T: From<u8>.此备用版本在此情况下也适用

fn is_odd<T: BitAnd<Output=T> + Eq + From<u8>>(x: T) -> bool {
    x & 1.into() == 1.into()
}

例如,如果您try 用T: i8来运行它,就不会这样(因为您可以将U8转换为i16,但不能将其转换为i8,因为一半的范围是无效的).

对于此特定角点情况,您可以使用TryFrom,方法是类似地设置T: TryFrom<u8>.然后使用ok().unwrap(),如果绑定了错误,则只使用1.try_into().unwrap():

fn is_odd<T>(x: T) -> bool
where
    T: BitAnd<Output = T> + Eq + TryFrom<u8>,
    <T as TryFrom<u8>>::Error: std::fmt::Debug,
{
    x & 1.try_into().unwrap() == 1.try_into().unwrap()
}

Rust相关问答推荐

在Tauri中获取ICoreWebView 2_7以打印PDF

为什么单元类型(空元组)实现了`Extend`trait?

包含嵌套 struct 的CSV

当Option为None时,Option数组是否占用Rust中的内存?

在Rust中宏的表达式中提取对象

编译项目期间使用Cargo生成时出现rustc错误

如何循环遍历0..V.len()-1何时v可能为空?

RUST应用程序正在退出,错误代码为:(退出代码:0xc0000005,STATUS_ACCESS_VIOLATION)

为什么这个变量不需要是可变的?

通过RabbitMQ取消铁 rust 中长时间运行的人造丝任务的策略

为什么Rust不支持带关联常量的特征对象?

返回Result<;(),框<;dyn错误>>;工作

在 Rust 中,为什么 10 个字符的字符串的 size_of_val() 返回 24 个字节?

将 &str 或 String 保存在变量中

在 Rust 中忽略 None 值的正确样式

go 重并堆积MPSC通道消息

我什么时候应该使用特征作为 Rust 的类型?

在每个循环迭代中删除borrow

为什么 i32 Box 类型可以在 Rust 中向下转换?

在 Rust 中退出进程