| 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.
    }
}
替代方案
以下替代方案可能更适合需要无界索引的情况(例如,向向量中索引)