如何转换使用Read特性的代码来改为使用Iterator特征?

问题描述 投票:2回答:3

我有一些解析文件的代码,非常类似于以下内容:

use std::io::Cursor;
use std::io::Result;
use std::io::Read;

fn main() {
   let v1 = [1, 2, 3, 4, 5];
   let mut c1 = Cursor::new(v1); // *

    println!("{:?}", read_one(&mut c1)); // 1
    println!("{:?}", read_three(&mut c1)); // 2, 3, 4     
    println!("{:?}", read_one(&mut c1)); // 5
    println!("{:?}", read_one(&mut c1)); // eof
}

fn read_one<R: Read>(r: &mut R) -> Result<u8> {
  let mut buf = [0; 1];
  r.read_exact(&mut buf)?;
  Ok(buf[0])
}

fn read_three<R: Read>(r: &mut R) -> Result<[u8; 3]> {
  let mut buf = [0; 3];
  r.read_exact(&mut buf)?;
  Ok(buf)
}

在这里,read_oneread_three采用Read特征,从中获取一些字节,并返回结果。然后我可以一块一块地读取字节。

我想将此代码转换为使用迭代器。也就是说,有*线

let mut i1 = v1.iter();

尝试这个,我遇到了一些问题,我找不到函数来轻松提取多个项目(类似于read_exact)或以其他方式部分处理迭代器。似乎大多数迭代器函数都是为了完全使用内容而设计的。

迭代器只是一个坏工具还是我使用它错了?如果是后者,如何将此代码转换为使用迭代器?

iterator rust
3个回答
1
投票

你可以用迭代器编写几乎相同的东西。您将遇到的问题是阵列很难使用,因为它们(截至2017年12月)没有实现FromIterator。作为user1541501 suggested,最简单的解决方案是将collect()改为Vec

fn main() {
    let v1 = vec![1, 2, 3, 4, 5];
    let mut c1 = v1.into_iter();

    println!("{:?}", read_one(&mut c1)); // 1
    println!("{:?}", read_three(&mut c1)); // 2, 3, 4
    println!("{:?}", read_one(&mut c1)); // 5
    println!("{:?}", read_one(&mut c1)); // eof
}

fn read_one<I: Iterator<Item = u8>>(i: &mut I) -> Option<u8> {
    i.next()
}

fn read_three<I: Iterator<Item = u8>>(i: &mut I) -> Result<Vec<u8>, Vec<u8>> {
    let buf: Vec<u8> = i.take(3).collect();
    if buf.len() == 3 {
        Ok(buf)
    } else {
        Err(buf)
    }
}

数组也没有实现IntoIterator,所以我把v1变成了Vec<u8>来调用v1.into_iter()。你可以写v1.iter().cloned()代替。

我做的另一个重大改变是让read_one返回Option而不是io::Result,并让read_three返回Result<Vec<u8>, Vec<u8>>。那是因为迭代器只能通过耗尽数据而失败。 (如果read_three失败,Err变体包含收集的元素。)


3
投票

你可以为此做出自己的特质

use std::io::{Error, ErrorKind, Result};

trait ReadExact {
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()>;
}

impl<T> ReadExact for T
where
    T: Iterator<Item = u8>,
{
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
        for p in buf {
            if let Some(b) = self.next() {
                *p = b;
            } else {
                return Err(Error::new(ErrorKind::UnexpectedEof, "Iterator exhausted"));
            }
        }
        Ok(())
    }
}

Playground


2
投票

这里是为什么你不能collect into an array的解释

如果您仍想使用迭代器,可以将返回类型更改为Vec并调用r.take(n).collect();

然而,这会慢一点。

© www.soinside.com 2019 - 2024. All rights reserved.