16 个版本
0.0.18 | 2024 年 6 月 21 日 |
---|---|
0.0.17 | 2024 年 6 月 17 日 |
0.0.14 | 2022 年 6 月 3 日 |
0.0.13 | 2022 年 4 月 30 日 |
0.0.9 | 2021 年 8 月 22 日 |
#249 in Rust 模式
34KB
337 行
andex
andex (数组索引) 是一个零依赖的 Rust crate,帮助我们创建具有提供大小的强类型、无成本的数值数组索引和相应的数组类型。索引是安全的,这意味着不能创建越界的值,数组类型也不能被任何其他类型索引。
这在以下场景中很有用,即我们在 struct 中有不同类型的数组,并希望引用成员而不需要持有可能会“锁定”整个 struct 的正确引用。在编写 实体组件系统 时也可能很有用。
而且这一切都无需使用任何宏。
用法
创建 andex 类型
Andex
是索引类型,AndexableArray
是数组包装器的类型。
使用 andex 的推荐方法是
- 创建一个唯一的空类型
enum MyIdxMarker {}
- 为
Andex
类型创建一个类型别名,该类型使用该类型参数化type MyIdx = Andex<MyIdxMarker, 12>;
- 为
AndexableArray
类型创建一个类型别名,该类型由上面创建的Andex
别名索引type MyU32 = AndexableArray<MyIdx, u32, { MyIdx::SIZE }>; // There is also a helper macro for this one: type MyOtherU32 = andex::array!(MyIdx, u32);
创建 andex 实例
当创建 andex 时,它知道在编译时索引的数组的大小,并且所有实例都假定在范围内。
因此,限制创建 Andex
的方式是有用的。获取实例的方法包括
-
通过
new
,传递值作为泛型常量参数const first : MyIdx = MyIdx::new::<0>();
这将在编译时检查值是否有效,只要您使用它创建
const
变量。 -
通过
try_from
,它返回一个Result<Andex, Error>
,必须进行检查或显式忽略if let Ok(first) = MyIdx::try_from(0) { // ... }
-
通过
FIRST
和LAST
const first : MyIdx = MyIdx::FIRST; let last = MyIdx::LAST;
-
通过迭代
for idx in MyIdx::iter() { // ... }
假设实例只能存储有效值,这使我们能够在索引器实现中使用 get_unsafe
和 get_unsafe_mut
,从而在索引时防止边界检查,提供了一定的优化。
创建可索引数组
AndexableArray
实例的约束较少。它们可以通过多种方式创建
- 如果基础类型支持,可以使用
Default
type MyU32 = AndexableArray<MyIdx, u32, { MyIdx::SIZE }>; let myu32 = MyU32::default(); // We also have a helper macro that avoids repeating the size: type MyOtherU32 = andex::array!(MyIdx, u32);
- 使用适当的数组与
From
let myu32 = MyU32::from([8; MyIdx::SIZE]);
- 收集具有适当元素和大小的迭代器
注意:如果迭代器返回不同数量的元素,则let myu32 = (0..12).collect::<MyU32>();
collect
会引发恐慌。
使用可索引数组
除了使用配对的 Andex
实例进行索引外,我们还可以通过使用 as_ref
来访问内部数组,在 for
循环中迭代(使用 IntoIterator
的实现之一)或者通过消耗 AndexableArray
来获取内部数组。
完整示例
use std::convert::TryFrom;
use std::error::Error;
use andex::*;
// Create the andex type alias:
// First, we need an empty type that we use as a marker:
enum MyIdxMarker {}
// The andex type takes the marker (for uniqueness)
// and the size of the array as parameters:
type MyIdx = Andex<MyIdxMarker, 12>;
// Create the array wrapper:
type MyU32 = AndexableArray<MyIdx, u32, { MyIdx::SIZE }>;
// We can create other arrays indexable by the same Andex:
type MyF64 = AndexableArray<MyIdx, f64, { MyIdx::SIZE }>;
fn main() -> Result<(), Box<dyn Error>> {
let myu32 = MyU32::default();
// We can now only index MyU32 using MyIdx
const first : MyIdx = MyIdx::new::<0>();
println!("{:?}", myu32[first]);
// Trying to create a MyIdx with an out-of-bounds value
// doesn't work, this won't compile:
// const _overflow : MyIdx = MyIdx::new::<30>();
// Trying to index myu32 with a "naked" number
// doesn't work, this won't compile:
// println!("{}", myu32[0]);
// We can create indexes via try_from with a valid value:
let second = MyIdx::try_from(2);
// ^ Returns a Result, which Ok(MyIdx) if the value provided is
// valid, or an error if it's not.
// We can also create indexes at compile-time:
const third : MyIdx = MyIdx::new::<1>();
// The index type has an `iter()` method that produces
// all possible values in order:
for i in MyIdx::iter() {
println!("{:?}", i);
}
Ok(())
}
编译时保证
这就是为什么在第一次使用时使用 Andex 而不是普通数组的原因,对吧?以下是我们在编译时获得的一些编译时限制列表。
-
我们无法使用
usize
索引AndexableArray
。以下代码无法编译
use andex::*;
enum MyIdxMarker {}
type MyIdx = Andex<MyIdxMarker, 12>;
type MyU32 = AndexableArray<MyIdx, u32, { MyIdx::SIZE }>;
fn main() {
let myu32 = MyU32::default();
// Error: can't index myu32 with a usize
println!("{}", myu32[0]);
}
-
我们无法使用越界值创建 const
Andex
。以下代码无法编译
use andex::*;
enum MyIdxMarker {}
type MyIdx = Andex<MyIdxMarker, 12>;
fn main() {
// Error: can't create out-of-bounds const:
const myidx : MyIdx = MyIdx::new::<13>();
}
-
即使它们具有相同的大小,我们也无法使用不同的 Andex 来索引
AndexableArray
。这就是使用不同标记所得到的结果。以下代码无法编译
use andex::*;
enum MyIdxMarker {}
type MyIdx = Andex<MyIdxMarker, 12>;
type MyU32 = AndexableArray<MyIdx, u32, { MyIdx::SIZE }>;
enum TheirIdxMarker {}
type TheirIdx = Andex<TheirIdxMarker, 12>;
type TheirU32 = AndexableArray<TheirIdx, u32, { TheirIdx::SIZE }>;
fn main() {
let myu32 = MyU32::default();
let theirIdx = TheirIdx::FIRST;
// Error: can't index a MyU32 array with TheirIdx
println!("{}", myu32[theirIdx]);
}
替代方案
这些替代方案可能更适合需要无界索引的情况(也许用于向量)