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))
遗憾的是,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))