我正在尝试创建一个泛型结构,它使用“整数类型”来引用数组。出于性能原因,我希望能够轻松指定是使用u16
,u32
还是u64
。像这样的东西(显然是无效的Rust代码):
struct Foo<T: u16 or u32 or u64> { ... }
有没有办法表达这个?
答案 0 :(得分:9)
对于数组的引用,通常只使用usize
而不是不同的整数类型。
但是,要在创建新特征后执行操作,请为u16
,u32
和u64
实施该特征,然后将T限制为新的特征。
pub trait MyNewTrait {}
impl MyNewTrait for u16 {}
impl MyNewTrait for u32 {}
impl MyNewTrait for u64 {}
struct Foo<T: MyNewTrait> { ... }
然后,您还可以在MyNewTrait
和impl
上添加方法,以封装特定于u16
,u32
和u64
的逻辑。
答案 1 :(得分:3)
有时您可能想使用enum
而不是带有特征绑定的泛型类型。例如:
enum Unsigned {
U16(u16),
U32(u32),
U64(u64),
}
struct Foo { x: Unsigned, ... };
与为现有类型实现新特征相比,使新类型具有一个优点是您可以向新类型添加外来特征和固有行为。您可以实现Unsigned
所需的任何特征,例如Add
,Mul
等,甚至是SliceIndex<[T]>
,以便可以将其用于索引切片。当Foo
包含Unsigned
时,在Unsigned
上实现特征不会影响Foo
的签名,就像将它们添加为Foo
的边界一样参数(例如Foo<T: Add<Output=Self> + PartialCmp + ...>
)。另一方面,您仍然必须实现每个特征。
要注意的另一件事:虽然通常可以总是创建一个新类型并为其实现特征,但是枚举是“封闭的”:您不能在不触及其他所有Unsigned
的情况下为其添加新类型。实现,就像使用特质一样。取决于您的设计要求,这可能是好事还是坏事。
“性能原因”有点模棱两可,但是如果您想存储很多Unsigned
,它们将都是相同的内部类型,并且:
struct Foo([Unsigned; 1_000_000]);
存储一百万个u16
会浪费大量空间,您仍然可以使Foo
通用!只需为From<u16>
实现From<u32>
,From<u64>
和Unsigned
并将其编写为:
struct Foo<T: Into<Unsigned>>([T; 1_000_000]);
现在,您在T
上只有一个简单的特征,您没有浪费标签和填充的空间,处理T
的函数可以始终将其转换为Unsigned
做计算。转换成本甚至可以完全被优化。