0.0.1 |
|
---|
#14 in #array-index
12KB
89 行
cludex
cludex (独占索引) 是一个单文件、无依赖的 Rust crate,它帮助我们创建强类型、零成本、安全的数组索引和相应的数组类型。
这在以下场景中特别有用:在 struct
中有多个数组,而我们又想引用成员而不保留“硬”引用。
基本用法
use cludex::*;
use cludex::impl_cludex_for;
// Create the type alias:
type MyIdx = Cludex<12>;
// Create the array wrapper:
#[derive(Default)]
pub struct MyU32([i32; MyIdx::SIZE]);
// Use `impl_cludex_for` to make it indexable:
impl_cludex_for!(MyU32, i32, MyIdx);
fn example() {
// Iterate:
for i in MyIdx::iter() {
println!("{:?}", i);
}
// Generate first index at compile time:
const first = MyIdx::new::<0>();
// Index the collection:
let myu32 = MyU32::default();
const first = MyIdx::new::<0>();
println!("{:?}", myu32[first]);
}
创建索引实例
当创建 clundex 时,它知道它所索引的数组的 编译时 大小,并且所有实例都被假定为在范围内。
因此,限制创建 clundex 的方式很有用。我们获取实例的方法有
-
通过
new
,传递值作为泛型 const 参数const first = Idx::new::<0>::()
这确保了值在编译时有效,只要你用它来创建
const
变量。 -
通过
try_from
,它返回一个Result<Cludex,Error>
,该结果需要检查或显式忽略if let Ok(first) = Idx::try_from(0) { ... }
-
通过迭代
for idx in Idx::iter() { ... }
实例只能持有有效值的假设使我们能够在索引器实现中使用 get_unsafe
和 get_unsafe_mut
,这通过在索引时防止边界检查提供了一些优化。
创建可索引数组包装器
要使用索引,我们首先创建数组包装器,然后使用 impl_cludex_for
使其可由 clundex 索引
pub struct ArrayWrapper([u32; 12])
impl_cludex_for!(ArrayWrapper, u32, Idx);
此宏创建适当的 Index
和 IndexMut
实现。这些实现使用 get_unchecked
和 get_unchecked_mut
进行包装,因为当创建 clundex 实例时已检查数组边界,我们无需再次检查。
注意:用户负责使 clundex 和包装器的限制相等。
完整示例
use cludex::*;
use std::convert::TryFrom;
/// A player with score
#[derive(Default)]
pub struct Player {
pub score: i32,
}
/// All players in the game
#[derive(Default)]
pub struct Players([Player; 4]);
/// The player identifier
type PlayerId = Cludex<4>;
// Make Players[PlayerId] work
impl_cludex_for!(Players, Player, PlayerId);
/// The game state
#[derive(Default)]
pub struct Game {
pub players: Players,
}
impl Game {
pub fn play(&mut self) {
// Increment all scores
for playerid in PlayerId::iter() {
self.players[playerid].score += 1;
}
// Increment the first player's score:
self.players[PlayerId::new::<0>()].score += 1;
// ^ note that we had to use a const generic parameter so that
// the index bound is checked at compile time.
// If we want to create an index at run time, we have to use
// TryInto/TryFrom, which returns Result:
if let Ok(playerid) = PlayerId::try_from(4) {
self.players[playerid].score = 3;
}
// ^ This "if" is never true because 4 >= 4, which is out-of-bounds.
}
}
替代方案
以下替代方案可能更适合需要无界索引的情况(例如,向向量中索引)