为什么 Read::read 和 Read::read_exact 要求初始化传递给它们的缓冲区?

zee*_*zee 7 rust

我有一个阅读器,其中包含有关 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);
    };
    //...
}
Run Code Online (Sandbox Code Playgroud)

此方法不方便且不必要地缓慢,因为它强制向量的所有元素都使用 0 值进行初始化。我最初想简单地分配一个缓冲区,而不是初始化它,将数据读入其中,然后用from_raw_parts它来创建一个向量。然而,我被告知这是未定义的行为,因为出于某种难以理解的原因,read并且read_exact要求调用者在调用它们中的任何一个之前初始化传递给它们的数据。

为什么会这样呢?有什么解决方法吗?Rust 团队正在研究任何解决方案吗?

She*_*ter 9

\n

为什么会这样呢?

\n
\n

Read因为对于 的实现者来说,首先读取传入的缓冲区是有效的。如果您传入未初始化的数据和实现者Read查看缓冲区,那么纯安全代码中将会出现未定义的行为。静态地禁止这种情况是 Rust 的一大卖点。

\n
use std::io::{self, Read};\n\nstruct Dummy;\n\nimpl Read for Dummy {\n    fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {\n        let v: u8 = buffer.iter().sum(); // Reading from the buffer\n        buffer[0] = v;\n        Ok(1)\n    }\n}\n\nfn main() {\n    let mut data = [0, 1, 2];\n    Dummy.read(&mut data).unwrap();\n    println!("{:?}", data);\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 为什么不Read::read阻止从缓冲区读取

    \n

    没有可用于施加该限制的语言结构。与其他一些语言不同,Rust 没有“输出参数”。即使确实如此,我也可以看到一个实现者Read希望能够读取它刚刚需要的数据。例如,一个读取器计算通过它的换行符的数量。

    \n
  • \n
  • 为什么不Read::read接受MaybeUninit

    \n

    MaybeUninitRust 1.0 \xe2\x80\x94 中不存在,仅在 Rust 1.36 中稳定。我们希望能够在 Rust 1.0 中读取文件。由于 Rust 的向后兼容性保证,该方法的签名现在无法更改。

    \n
  • \n
  • 为什么Read::read不是unsafe

    \n

    这将是支持未初始化数据的主要(唯一?)技术,但成本很高。unsafe 这不是经验丰富的 Rust 程序员会轻易选择的工具。当我们确实使用它时,我们通常会努力缩小其范围。

    \n

    如果Read::read不安全,那么每个实施者都必须考虑如何正确满足不安全标准。这对“简单”适配器来说是一个沉重的负担。

    \n
  • \n
\n
\n

有什么解决方法吗?Rust 团队正在研究任何解决方案吗?

\n
\n

不稳定Read::initializer方法是一种建议的解决方案,但它可能不是首选途径。

\n

RFC 2930提供了更新的尝试,并讨论了许多背景故事和挑战。

\n

也可以看看:

\n\n
\n

对于您的具体情况,您可能可以使用Read::takeRead::read_to_end将您想要的所有字节读入空(未初始化!)Vec,然后将 a 转换Vec<T>为 aVec<U>而不复制向量。您需要以某种方式确保您已正确对齐Vecfor f32,因为它一开始仅与 for 对齐u8

\n