【问题标题】:Is there a simpler way to pass a BufReader to a function?有没有更简单的方法将 BufReader 传递给函数?
【发布时间】:2022-01-03 23:50:59
【问题描述】:

为了读取 PNG 文件的字节,我想创建一个名为 read_8_bytes 的函数,每次调用它都会读取文件中接下来的 8 个字节。

fn main(){
   let png = File::open("test.png").expect("1");
   let mut png_reader = BufReader::new(png);
   let mut byteBuffer: Vec<u8> = vec![0;8];
   png_reader.read_exact(&mut byteBuffer).expect("2"); 
}

这很好用,如果我继续从 main 调用 read_exact,我可以读取接下来的 8 个字节。我试图创建一个函数来执行此操作,但解决方案似乎不必要地复杂。我想知道是否有更好的方法。

我认为我必须将 BufReader 传递给函数,但由于 Rust 的工作方式,这使事情变得复杂,我最终发现我需要做一些类似的事情:

fn read_eight_bytes<R: BufRead>(fd: &mut R)

这可以编译,但我不高兴,因为我不明白为什么需要这样做并且看起来很复杂。有没有一种简单的方法可以让我可以将文件描述符类型的东西传递给它并让它像在 C 中一样存储位置而不必这样做?

【问题讨论】:

  • 我不清楚这其中的哪一部分是“复杂的”,它看起来像是一个完全合理的函数。
  • 同意,除了 fn read_eight_bytres(fd: &amp;mut impl BufRead) 你的 fn 对我来说似乎完全没问题。
  • 我发现它很复杂,因为我希望来自 c 能够将对 BufReader 的引用传递给函数并使用它来引用数据,但我必须使用泛型并使用 BufRead 。 .loganfsmyth 之前我没见过,我可以像你说的那样声明一个函数并让它工作吗?

标签: rust file-io


【解决方案1】:

看着你的问题,我想你是想说你很困惑为什么&lt;R: BufRead&gt; 是必要的,或者为什么这甚至有效。

在你的例子中,这个 generic 并不是绝对必要的。可以像这样实现您描述的功能:

use std::{fs, io};

fn main() -> io::Result<()> {
    let mut file = fs::File::open("./path/to/file")?;
    let bytes = read_eight_bytes(&mut file)?;
    println!("{:?}", bytes);
    Ok(())
}

fn read_eight_bytes(file: &mut fs::File) -> io::Result<[u8; 8]> {
    use io::Read;

    let mut bytes = [0; 8];
    file.read_exact(&mut bytes)?;
    Ok(bytes)
}

Playground

这是完全有效的,希望应该是有意义的。

但是,为什么fn read_eight_bytes&lt;R: BufRead&gt;(file: &amp;mut R) -&gt; [u8; 8] 有效?首先,我假设您了解以下概念:

了解上述概念后,您应该知道,这种语法意味着函数read_eight_bytes 是一个泛型函数,其泛型类型名为R。然后您还应该了解 generic 具有 trait bound,需要类型 R 来实现 BufRead。并且该函数接受一个参数,该参数是对变量file 的可变引用,该变量的类型为R

现在看一下BufRead 的定义:我们看到它包含几个函数。但令人惊讶的是没有read_exact 功能!为什么这样的函数会编译?

use std::{fs, io};
use io::BufRead;

fn main() -> io::Result<()> {
    let file = fs::File::open("./path/to/file")?;
    let mut reader = io::BufReader::new(file);
    let bytes = read_eight_bytes(&mut reader)?;
    println!("{:?}", bytes);
    Ok(())
}

fn read_eight_bytes<R: BufRead>(reader: &mut R) -> io::Result<[u8; 8]> {
    let mut bytes = [0; 8];
    reader.read_exact(&mut bytes)?;
    Ok(bytes)
}

Playground

注意:我已将返回类型更改为io::Result&lt;...&gt;。与unwraping 每Result 相比,这被认为是一种更好的做法。 我还更改了函数调用以使用BufReader,因为BufReader 实现BufReadFile 没有。我将在下面进一步介绍差异。

之所以有效是因为BufReadSuper Trait。这意味着任何实现BufRead 的类型也必须实现Read。因此它必须具有read_exact 功能!

鉴于我们的函数从不需要 BufRead 上的函数,我们可以将 trait 绑定更改为仅需要 Read

use std::{fs, io};
use io::Read;

fn main() -> io::Result<()> {
    let file = fs::File::open("./path/to/file")?;
    let mut reader = io::BufReader::new(file);
    let bytes = read_eight_bytes(&mut reader)?;
    println!("{:?}", bytes);
    Ok(())
}

fn read_eight_bytes<R: Read>(reader: &mut R) -> io::Result<[u8; 8]> {
    let mut bytes = [0; 8];
    reader.read_exact(&mut bytes)?;
    Ok(bytes)
}

Playground

现在这里有一些关于此更改的有趣内容。 read_eight_bytes 函数现在可以(至少)以两种不同的方式调用:

use std::{fs, io};
use io::Read;

fn main() -> io::Result<()> {
    let mut file = fs::File::open("./path/to/file")?;
    let bytes = read_eight_bytes(&mut file)?;
    println!("{:?}", bytes);

    let file = fs::File::open("./path/to/file")?;
    let mut reader = io::BufReader::new(file);
    let bytes = read_eight_bytes(&mut reader)?;
    println!("{:?}", bytes);

    Ok(())
}

fn read_eight_bytes<R: Read>(reader: &mut R) -> io::Result<[u8; 8]> {
    let mut bytes = [0; 8];
    reader.read_exact(&mut bytes)?;
    Ok(bytes)
}

Playground

这是为什么?这是因为 FileBufReader 都实现了 Read 特征。因此两者都可以与read_eight_bytes 函数一起使用!

那么为什么有人要使用FileBufReader 而不是另一个呢?

BufReader 文档对此进行了解释:

BufReader 结构为任何读取器添加缓冲。

直接使用 Read 可能效率极低 实例。例如,对 TcpStream 的每次读取调用都会导致 系统调用。 BufReader 在 底层读取并维护结果的内存缓冲区。

BufReader 可以提高小程序的速度 对同一文件或网络套接字的重复读取调用。它不是 一次阅读大量内容或仅阅读一个或一个 几次。从源读取时它也没有任何优势 已经在内存中了,就像一个 Vec。

现在,还记得我们之前是如何为File 类型编写这个函数的吗?人们想要用泛型编写它的主要原因是调用者可以做出上述选择。这是图书馆的常见做法,这种选择确实很重要。然而,泛型是以增加compile times(过度使用时)和增加代码复杂性为代价的。

【讨论】:

  • 感谢您抽出宝贵的时间来做这件事,我想这只是生锈的一部分,我必须学会在某些方面理解 C 更容易编写这些东西,但我会继续练习,我通过示例和其他工作完成了全部生锈,老实说,特征是我不真正理解的一件事 100%
  • 很高兴听到这个答案很有帮助 :) 祝你在 rust 学习之旅中好运!顺便说一句,对于其他资源(关于特质等),我可以推荐任何链接到 rust-lang.org/learn 的东西,尤其是“书”。
猜你喜欢
  • 2021-08-21
  • 2015-12-20
  • 2010-12-07
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
  • 2021-12-26
  • 2022-01-03
相关资源
最近更新 更多