Rust 中固定大小字节数组的人体工程学问题

Ergonomics issues with fixed size byte arrays in Rust

遗憾的是,Rust 无法使用固定大小的切片运算符 s[0..16] 生成固定大小的数组 [u8; 16]。它会抛出错误,例如“预期的 16 元素数组,找到切片”。

我有一些 KDF 可以在包装器 struct 中输出几个键,例如

pub struct LeafKey([u8; 16]);
pub struct MessageKey([u8; 32]);

fn kdfLeaf(...) -> (MessageKey,LeafKey) {
    // let mut r: [u8; 32+16];
    let mut r: (MessageKey, LeafKey);
    debug_assert_eq!(mem::size_of_val(&r), 384/8);
    let mut sha = Sha3::sha3_384();
    sha.input(...);

    // sha.result(r);
    sha.result( 
      unsafe { mem::transmute::<&mut (MessageKey, LeafKey),&mut [u8;32+16]>(&r) } 
    );
    sha.reset();

    // (MessageKey(r[0..31]), LeafKey(r[32..47]))
    r
}

有更安全的方法吗?我们知道 mem::transmute 将拒绝编译如果类型不具有相同的大小,但这里只检查指针是否具有相同的大小,所以我添加了 debug_assert.

事实上,我并不十分担心额外的副本,因为我在这里 运行 SHA3,但是 afaik Rust 没有提供符合人体工程学的方式来在字节数组之间进行复制。

这里可以不写三遍(MessageKey, LeafKey)吗?当前函数的 return 类型是否有类型别名?鉴于我希望代码在大小不匹配时拒绝编译,在 mem::transmute 中使用 _ 是否安全?是的,我知道我可以做一个类型别名,但这看起来很傻。

顺便说一句,关于 s[0..16] 没有类型 [u8; 16] here

的讨论更长

copy_from_slice方法。

fn main() {
    use std::default::Default;

    // Using 16+8 because Default isn't implemented
    // for [u8; 32+16] due to type explosion unfortunateness
    let b: [u8; 24] = Default::default();
    let mut c: [u8; 16] = Default::default();
    let mut d: [u8; 8] = Default::default();

    c.copy_from_slice(&b[..16])
    d.copy_from_slice(&b[16..16+8]);
}

注意,不幸的是,如果切片的长度不同,copy_from_slice 会抛出 runtime 错误,因此请确保您自己彻底测试了这一点,或者使用其他要守卫的阵列。

不幸的是,c.copy_from_slice(&b[..c.len()]) 不起作用,因为 Rust 认为 c 同时被不可变和可变借用。

我将接受的答案标记为最佳,因为它是安全的,并引导我找到 clone_into_array 答案 ,但是..

另一个提高安全性的想法是为引用制作一个版本的 mem::transmute 检查引用类型的大小,而不是仅仅检查指针。它可能看起来像:

#[inline]
unsafe fn transmute_ptr_mut<A,B>(v: &mut A) -> &mut B {
    debug_assert_eq!(core::mem::size_of(A),core::mem::size_of(B));
    core::mem::transmute::<&mut A,&mut B>(v)
}

我有 raised an issue on the arrayref crate 来讨论这个问题,因为 arrayref 可能是一个适合它居住的板条箱。

更新:我们有一个新的 "best answer" 由 arrayref crate 开发者:

let (a,b) = array_refs![&r,32,16];
(MessageKey(*a), LeafKey(*b))