14 个版本

使用旧的 Rust 2015

0.3.8 2024年7月20日
0.3.7 2023年3月20日
0.3.6 2020年1月26日
0.3.5 2018年8月16日
0.3.2 2015年9月21日

#32 in Rust 模式

Download history 513557/week @ 2024-05-04 584073/week @ 2024-05-11 582196/week @ 2024-05-18 596640/week @ 2024-05-25 642439/week @ 2024-06-01 593872/week @ 2024-06-08 585567/week @ 2024-06-15 633410/week @ 2024-06-22 587283/week @ 2024-06-29 618082/week @ 2024-07-06 633227/week @ 2024-07-13 681833/week @ 2024-07-20 675973/week @ 2024-07-27 653927/week @ 2024-08-03 685810/week @ 2024-08-10 552486/week @ 2024-08-17

2,678,116 次每月下载
用于 6,444 个 crate (330 个直接使用)

BSD-2-Clause

23KB
327

arrayref

Build Status Coverage Status

文档

这是一个非常小的 Rust 模块,其中只包含四个宏,用于获取可切片事物的切片的数组引用。这些宏(命名有些尴尬)应该是完全安全的,并且已经经过一点代码审查。

为什么需要这个呢?

arrayref 的目标是使涉及数组引用而不是切片的 API 的有效使用成为可能,适用于参数必须具有特定大小的场景。以 byteorder crate 为例。这是一个非常好的 crate,具有简单的 API,其中包含看起来像这样的函数:

fn read_u16(buf: &[u8]) -> u16;
fn write_u16(buf: &mut [u8], n: u16);

看到这个,你可能会想知道为什么它们接受切片引用作为输入。毕竟,它们总是只需要两个字节。如果给定的切片太小,这些函数必须 panic,这意味着即使可以静态地知道输入是正确的尺寸,也必须强制执行运行时边界检查。

如果我们有更像是这样的函数,岂不是更好?

fn read_u16_array(buf: &[u8; 2]) -> u16;
fn write_u16_array(buf: &mut [u8; 2], n: u16);

类型签名将精确地告诉用户所需的输入大小,编译器可以在编译时检查输入是否为适当的尺寸:这正是 Rust 以零成本抽象而闻名的地方!然而,有一个问题,当你尝试使用这些更好的函数时,通常你正在查看流中的两个字节。例如,考虑我们正在处理一个假设的(简化了的)ipv6 地址。

使用我们的数组版本(从准确描述我们想要的内容来看,看起来非常漂亮!)看起来很糟糕

let addr: &[u8; 16] = ...;
let mut segments = [0u16; 8];
// array-based API
for i in 0 .. 8 {
    let mut two_bytes = [addr[2*i], addr[2*i+1]];
    segments[i] = read_u16_array(&two_bytes);
}
// slice-based API
for i in 0 .. 8 {
    segments[i] = read_u16(&addr[2*i..]);
}

基于数组的方案看起来要糟糕得多。我们需要创建字节数组的新副本,只是为了确保它位于正确大小的数组中!因此,使用数组引用的“零成本抽象”论点完全失败。问题在于,在没有RFC 495落地的情况下,没有(安全)的方法来获取大数组的某个部分或切片的数组引用。这样做等价于获取一个在编译时已知大小的切片,并且应该内置于语言中。

arrayref crate 允许你进行此类切片操作。因此,上面的(非常牵强的)示例可以用数组引用实现

let addr: &[u8; 16] = ...;
let mut segments = [0u16; 8];
// array-based API with arrayref
for i in 0 .. 8 {
    segments[i] = read_u16_array(array_ref![addr,2*i,2]);
}

在这里,array_ref![addr,2*i,2] 宏允许我们获取一个从 2*i 开始的由两个字节组成的切片。除了语法(不如切片那样优雅)外,它本质上与切片方法相同。然而,此代码在调用者和函数签名中都明确表示需要恰好两个字节。

此模块还提供了三个其他宏,提供相关功能,你可以在文档中了解它们。

关于这些宏如何在实际程序中使用,请参阅我的 tweetnacl Rust 翻译示例,它使用 arrayref 几乎只接受函数中的数组引用,唯一的例外是那些真正期望任意长度数据的函数。在我看来,结果代码比原始 C 代码更易于阅读,因为每个参数的大小都是明确的。此外(尽管我尚未测试),与使用切片相比,使用数组引用应该会大大减少边界检查,因为几乎所有大小都是在编译时已知的。

无运行时依赖