rust - 如何从 `&mut [u32]` 安全地获取不可变字节切片?
问题描述
在我的项目的一个相当低级的部分中,一个函数接收一个可变的原始数据切片(&mut [u32]
在这种情况下)。该数据应以小端序写入写入器。
现在,仅此一项不会成为问题,但所有这些都必须快速。我测量了我的应用程序并将其确定为关键路径之一。特别是,如果不需要更改字节序(因为我们已经在一个小字节序系统上),则不应该有任何开销。
这是我的代码(游乐场):
use std::{io, mem, slice};
fn write_data(mut w: impl io::Write, data: &mut [u32]) -> Result<(), io::Error> {
adjust_endianness(data);
// Is this safe?
let bytes = unsafe {
let len = data.len() * mem::size_of::<u32>();
let ptr = data.as_ptr() as *const u8;
slice::from_raw_parts(ptr, len)
};
w.write_all(bytes)
}
fn adjust_endianness(_: &mut [u32]) {
// implementation omitted
}
adjust_endianness
改变了适当的字节序(这很好,因为错误的字节序u32
是垃圾,但仍然是有效的u32
)。
此代码有效,但关键问题是:这安全吗?特别是,在某些时候,data
两者bytes
都存在,对于相同的数据是一个可变切片和一个不可变切片。这听起来很糟糕,对吧?
另一方面,我可以这样做:
let bytes = &data[..];
这样,我也有那两片。不同的是,data
现在是借来的。
我的代码是安全的还是显示 UB?为什么?如果不安全,如何安全地做我想做的事?
解决方案
一般来说,创建违反 Rust 安全规则的切片,即使是短暂的,也是不安全的。如果你欺骗借用检查器并让独立的切片同时借用相同的数据&
,&mut
这将使 Rust 在 LLVM 中指定错误的别名信息,这可能会导致实际错误编译的代码。Miri没有标记这种情况,因为您之后没有使用,但仍在制定data
不安全的确切细节。
为了安全起见,您应该向借阅检查员解释共享情况:
let shared_data = &data[..];
data
将在使用期间临时重新借用为共享/只读shared_data
。在这种情况下,它不应该造成任何限制。退出此范围后,data
将保持可变。
然后你会有&[u32]
,但你需要&[u8]
。幸运的是,这种转换是安全的,因为两者都是共享的,并且u8
对齐要求比u32
(如果是另一种方式,你必须使用align_to
!)。
let shared_data = &data[..];
let bytes = unsafe {
let len = shared_data.len() * mem::size_of::<u32>();
let ptr = data.as_ptr() as *const u8;
slice::from_raw_parts(ptr, len)
};
推荐阅读
- c# - {"ORA-06502: PL/SQL: 数字或值错误: 字符到数字的转换错误\nORA-06512: 在第 1 行"}
- c# - 是否可以在 Windows 窗体应用程序的 WebViewControl 中获取 html 代码?
- python - Python 类的子类可以有与基类不同的参数吗?
- google-cloud-platform - 使用 Cloud Build 服务帐号进行编程式身份验证
- c# - 绑定不适用于应用程序资源
- python - 熊猫数据框中的跨列搜索
- r - 如何找到事件/字母/的发生模式?
- php - 无法在 PHP 中获取查询字符串(初学者/简单问题?)
- gradle - 如何编写一个 gradle 构建任务,该任务在与我的 gradle 构建文件不同的目录中运行诸如“npm install”之类的脚本?
- javascript - 在 PHP 中返回没有值