提问人:user52366 提问时间:11/4/2023 更新时间:11/5/2023 访问量:56
无法为整数类型实现泛型 FN
cannot implement generic fn for integer types
问:
我在 C++ 方面经验丰富,并开始玩 rust。
尝试实现一些简单的泛型函数时,我遇到了以下问题:
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));
}
问题似乎是将按位和结果与 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++ 的模板完全不同的工作原理。
在 Rust 的泛型中,约束是你唯一可以依赖的东西。所以当你写的时候,这就是能力的范围。这里甚至没有任何东西是数字相邻的,因为据你所知,有人为了方便起见而在哈希集上实现。因此,你的强制转换对编译器来说完全没有意义(强制转换首先不是泛型操作),因此它的抱怨。T: BitAnd + Eq
T
T
&
要使用泛型,你需要一种通用的方式来表达这些操作,比如“数字塔”,它将所有基本数字操作定义为泛型,但早在早期,核心团队就认为这不是一个对核心语言和标准库有用的努力,过去几乎没有的东西被删除/移动到 num 生态系统中。为此,特别是 One
特征:
fn is_odd<T: BitAnd + Eq + One>(x: &T) -> bool {
*x & T::one() == T::one()
}
(我删除了无用的语法)
当然,如果你应用它,你会发现更多的问题:
BitAnd
不保证其输出与其输入有任何关系,因此您需要将其限制在有意义的内容中,一种选择是仅要求返回:T & T
T
fn is_odd<T: BitAnd<Output=T> + Eq + One>(x: &T) -> bool { *x & T::one() == T::one() }
你不能把东西从引用中移出,这正是这里试图做的事情。您可以限制类型(允许复制引用),或者只是不进行引用,我选择了后者,因为我不明白重点:
*x
x
Copy
x
fn is_odd<T: BitAnd<Output=T> + Eq + One>(x: T) -> bool { x & T::one() == T::one() }
好了,如果你修复调用站点以删除引用,它现在可以工作了。 现在可以像你想要的那样灵活地工作(如果一个类型碰巧以一种没有意义的方式实现所有三个操作,这可能是完全不连贯的,这可能就是为什么is_odd
作为 Integer 特征的非默认操作的原因)。is_odd
num
顺便说一句,在这种情况下,您的 println 是过于复杂的 dbg
版本:
dbg!(is_odd(23_u64));
=>
[src/main.rs:9] is_odd(23_u64) = true
顺便说一句,您也可以使用转换版本(不使用 ),但是在这种情况下,如最初所述,您需要告诉编译器您需要确切的转换,这里需要从 u8 转换,又名 .在这种情况下,此替代版本也有效num
T
T: From<u8>
fn is_odd<T: BitAnd<Output=T> + Eq + From<u8>>(x: T) -> bool {
x & 1.into() == 1.into()
}
例如,如果您尝试运行它,则不会(因为您可以将 U8 转换为 i16,但不能转换为 i8,因为一半的范围无效)。T: 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()
}
评论