这可能是Rust 的一个超级基本方面,但我正在努力看透参考判断的级别,以使其有意义.

假设我当前的程序对文件中所有二进制编码的整数求和:

pub fn read_int(file: &mut File) -> io::Result<i64> {
  let mut int_buffer = [0; 8];
  file.read_exact(&mut int_buffer)?;
  Ok(i64::from_be_bytes(int_buffer))
}

fn read_loop() -> io::Result<f64> {
    let mut file = File::open("data.dat")?;
    let mut found_end = false;
    let mut dat_sum = 0;
    let mut dat_count = 0;
    while !found_end {
        match read_int(&mut file) {
            Ok(d) => {
                dat_sum   += d;
                dat_count += 1;
            },
            Err(_) => found_end = true
        };
    }
    Ok(dat_sum as f64 / dat_count as f64)
}

我想对此进行抽象,以使用专门的i64迭代器:

fn read_loop() -> io::Result<f64> {
    let mut int_iter = IntIterator::open("data.dat")?;
    let mut dat_sum = 0;
    let mut dat_count = 0;
    for d in iiter {
        dat_sum   += d;
        dat_count += 1;
    }
    Ok(dat_sum as f64 / dat_count as f64)
}

我try 实现此功能,但:

pub struct TickIterator<'r> {
    file: &'r mut File,
    found_end: bool
}

impl<'r> TickIterator<'r> {
    pub fn new(fp: String) -> io::Result<TickIterator<'r>> {
      let mut file = File::open(fp)?;
      Ok(TickIterator {
          file: &mut file,
          found_end: false
      })
    }
}

impl<'a> Iterator for TickIterator<'a> {
    type Item = i64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.found_end {
            None
        } else {
            match read_int(self.file) {
                Err(_) => {
                    self.found_end = true;
                    None
                },
                Ok(d) => Some(d)
            }
        }
    }
}

看起来我有很多问题要解决;第一个错误似乎是,我无法在 struct 内返回对file的引用,因为它属于函数new本身.

这是正确的方法,还是我在这里做错了?

推荐答案

这里有一种方法可以帮助您做到这一点.这里的诀窍是完全忽略生存期,将类型留给泛型.我们真的不需要知道我们从中读取的数字是什么,因为我们只需要std::io::Read就可以实现该类型.通过这种方法,您可以使用FileTcpStreamBufReader和许多其他类型的 crate ,这些 crate 也使用Read.&mut T是一种与T类似的类型,如果我们查看文档,那么所有&mut T where T: Read都实现了Read.

use std::io::{self, ErrorKind, Read};

pub struct ValueIter<R> {
    reader: R,
    found_end: bool,
}

impl<R: Read> ValueIter<R> {
    pub fn for_reader(reader: R) -> Self {
        ValueIter { 
            reader,
            found_end: false,
        }
    }
}

// `Read` is the trait that gives us `read_exact` 
impl<R: Read> Iterator for ValueIter<R> {
    type Item = io::Result<i64>;
    
    fn next(&mut self) -> Option<Self::Item> {
        if self.found_end {
            return None
        }
        
        let mut buffer = [0u8; 8];
        match self.reader.read_exact(&mut buffer) {
            // Read the next number
            Ok(_) => Some(Ok(i64::from_be_bytes(buffer))),
            // Handle end of file reached
            Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
                self.found_end = true;
                None
            }
            // Return any other errors as part of the iterator
            Err(e) => Some(Err(e)),
        }
    }
}

然后它可以像这样使用.

pub fn read_loop<R: Read>(reader: &mut R) -> io::Result<i64> {
    let mut sum = 0;
    let mut count = 0;
    
    for value in ValueIter::for_reader(reader) {
        sum += value?;
        count += 1;
    }
    
    Ok(sum / count)
}

值得注意的是,既然我们让它通用了Read多个,那么我们就可以使用File多个.例如,您可以通过将文件包装在BufReader中来提高性能,这样它就可以从操作系统中获取更大的文件块,而无需在每次读取新数字时询问操作系统.

use std::fs::File;
use std::io::BufReader;
let mut file = BufReader::new(File::open("data.dat").expect("Unable to open file"));

println!("{:?}", read_loop(&mut file).unwrap());

Rust相关问答推荐

即使参数和结果具有相同类型,fn的TypId也会不同

如何使用盒装枚举进行模式匹配?

自定义结果枚举如何支持`?`/`FromResidual`?

创建Rust中元对象协议的动态对象 Select /重新分配机制

如何在AVX2中对齐/旋转256位向量?

对reqwest提供的这种嵌套JSON struct 进行反序列化

找不到 .has_func 或 .get_func 的 def

随机函数不返回随机值

可选包装枚举的反序列化

std mpsc 发送者通道在闭包中使用时关闭

仅在使用 &mut 或线程时borrow 的数据在闭包之外转义?

go 重并堆积MPSC通道消息

如何使用 Rust Governor 为每 10 秒 10 个请求创建一个 RateLimiter?

有什么方法可以通过使用生命周期来减轻嵌套生成器中的当生成器产生时borrow 可能仍在使用错误?

由特征键控的不同 struct 的集合

在 Bevy 项目中为 TextureAtlas 精灵实施 NearestNeighbor 的正确方法是什么?

如果不满足条件,如何在 Rust 中引发错误

在 Rust 中如何将值推送到枚举 struct 内的 vec?

如何在 Rust 中返回通用 struct

在 macro_rules 中转义 $ 美元符号