我有一个阅读器,它包含关于51*51网格的信息,其中网格上的每个点都由一个f32表示。我希望将这些数据读入向量中,以便我能够轻松地处理它:
pub fn from_reader<R: Read + Seek>(reader: &mut R) -> Arena {
let arena_size = 51 * 51;
let arena_byte_size = arena_size * size_of::<f32>();
let mut arena = vec![0.0f32; arena_size];
unsafe {
let mut arena_slice =
std::slice::from_raw_parts_mut(arena.as_mut_ptr() as *mut u8, arena_byte_size);
let _ = reader.read(&mut arena_slice);
};
//...
}该方法不方便,而且速度过慢,因为它强制所有元素用0值初始化向量。我最初只想简单地分配一个缓冲区,而不是初始化它,将数据读入其中,然后使用from_raw_parts创建一个向量。但是,我被告知这是未定义的行为,因为出于某种无法理解的原因,read和read_exact要求调用方在调用它们之前初始化传递给它们的数据。
为什么是这种情况?有什么解决办法吗?铁锈小组有什么解决办法吗?
发布于 2021-12-03 21:50:44
为什么是这种情况?
因为对Read的实现者来说,首先读取传入缓冲区是有效的。如果您传入未初始化的数据,而Read的实现者查看了缓冲区,那么在纯安全代码中就会出现未定义的行为。静态地否定这一点,是锈病的一大卖点。
use std::io::{self, Read};
struct Dummy;
impl Read for Dummy {
fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
let v: u8 = buffer.iter().sum(); // Reading from the buffer
buffer[0] = v;
Ok(1)
}
}
fn main() {
let mut data = [0, 1, 2];
Dummy.read(&mut data).unwrap();
println!("{:?}", data);
}Read::read 不阻止从缓冲区读取?
没有一种语言结构可以用来施加这种限制。与其他语言不同,Rust没有"out参数“。即使是这样,我也可以看到Read的实现者希望能够读取它刚刚编写的数据。例如,计算通过它的换行符数的读取器。Read::read 不接受 MaybeUninit**?**?
MaybeUninit不存在于锈蚀1.0中,它只在锈菌1.36中稳定。我们希望能够从Rust 1.0中的文件中读取。由于Rust的向后兼容保证,该方法的签名现在不能改变。Read::read 不是 unsafe**?**?
这将是主要的(只有?)支持未初始化数据的技术,但代价很高。unsafe不是一个经验丰富的Rust程序员所选择的工具。当我们使用它时,我们通常会非常努力地尽量减少它的范围。
如果Read::read是不安全的,那么每个实现者都必须考虑如何正确地满足不安全的标准。对于“简单”适配器来说,这是一个很高的负担。有什么解决办法吗?铁锈小组有什么解决办法吗?
不稳定的Read::initializer方法是一种建议的解决方案,但它可能不是首选的路径。
RFC 2930提供了一个更新的尝试,并讨论了许多背景和挑战。
另请参阅:
对于您的具体情况,您可能可以使用Read::take和Read::read_to_end将您想要的所有字节读入一个空的(未初始化的!) Vec,然后是而不复制向量。您需要以某种方式确保Vec for f32已经正确地对齐,因为它开始时只对u8。
https://stackoverflow.com/questions/70220906
复制相似问题