【发布时间】:2016-11-23 23:47:59
【问题描述】:
我正在尝试创建一个通用结构,它使用“整数类型”来引用数组。出于性能原因,我希望能够轻松指定是否使用u16、u32 或u64。像这样的东西(这显然不是有效的 Rust 代码):
struct Foo<T: u16 or u32 or u64> { ... }
有什么办法可以表达吗?
【问题讨论】:
标签: rust
我正在尝试创建一个通用结构,它使用“整数类型”来引用数组。出于性能原因,我希望能够轻松指定是否使用u16、u32 或u64。像这样的东西(这显然不是有效的 Rust 代码):
struct Foo<T: u16 or u32 or u64> { ... }
有什么办法可以表达吗?
【问题讨论】:
标签: rust
对于数组的引用,通常您只需使用 usize 而不是不同的整数类型。
但是,要在创建新 trait 后做自己的事情,请为 u16、u32 和 u64 实现该 trait,然后将 T 限制为您的新 trait。
pub trait MyNewTrait {}
impl MyNewTrait for u16 {}
impl MyNewTrait for u32 {}
impl MyNewTrait for u64 {}
struct Foo<T: MyNewTrait> { ... }
您还可以在MyNewTrait 和impls 上添加方法来封装特定于u16、u32 和u64 的逻辑。
【讨论】:
pub trait MyNewTrait: Add<Output = Self> + Mul<Output = Self> + ... {}
Into<usize> 的结构或特征添加另一个特征限制。 T: MyNewTrait + Into<usize> 或 trait MyNewTrait : Into<usize>
有时您可能希望使用enum 而不是具有特征绑定的泛型类型。例如:
enum Unsigned {
U16(u16),
U32(u32),
U64(u64),
}
struct Foo { x: Unsigned, ... };
与为现有类型实现新特征相比,创建新类型的一个优点是您可以将外来特征和固有行为添加到新类型。您可以为Unsigned 实现任何您喜欢的特征,例如Add、Mul 等。当Foo 包含Unsigned 时,在Unsigned 上实现特征不会影响Foo 的签名,例如它会将它们添加为Foo 参数的边界(例如Foo<T: Add<Output=Self> + PartialCmp + ...>)。另一方面,您仍然必须实现每个特征。
另一件需要注意的事情:虽然你通常可以创建一个新类型并为它实现一个特征,但枚举是“关闭的”:你不能在不触及其实现的其余部分的情况下向Unsigned 添加新类型,就像你使用特质一样。这可能是好事也可能是坏事,具体取决于您的设计要求。
“性能原因”有点模棱两可,但如果您考虑存储大量 Unsigneds,它们都将是相同的内部类型,那么:
struct Foo([Unsigned; 1_000_000]);
存储一百万个u16s 会浪费大量空间,您仍然可以将Foo 设为通用!只需为 Unsigned 实现 From<u16>、From<u32> 和 From<u64>,然后改写:
struct Foo<T: Into<Unsigned>>([T; 1_000_000]);
现在您只在T 上绑定了一个简单的特征,您不会为标签和填充浪费空间,并且处理T 的函数始终可以将其转换为Unsigned 以进行计算。甚至可以完全优化转换成本。
【讨论】:
num 板条箱已经为您完成了这项工作:docs.rs/num/latest/num/traits/trait.Unsigned.html