#array-index #reference #safe #strongly-typed #alias #ex-cl-usive #in-de-xes

已删除 cludex

为 Rust 数组提供强类型、零成本、安全的独占索引

0.0.1 2021年7月27日

#14 in #array-index

MIT 许可证

12KB
89

CI coveralls

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_unsafeget_unsafe_mut,这通过在索引时防止边界检查提供了一些优化。

创建可索引数组包装器

要使用索引,我们首先创建数组包装器,然后使用 impl_cludex_for 使其可由 clundex 索引

pub struct ArrayWrapper([u32; 12])

impl_cludex_for!(ArrayWrapper, u32, Idx);

此宏创建适当的 IndexIndexMut 实现。这些实现使用 get_uncheckedget_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.
    }
}

替代方案

以下替代方案可能更适合需要无界索引的情况(例如,向向量中索引)

无运行时依赖