rust - 如何减少 std::io::Chain

标签 rust

https://doc.rust-lang.org/rust-by-example/std_misc/file/read_lines.html 开始,我想定义一个函数,它接受一个可迭代的路径,并返回一个将所有路径包装到单个流中的 Reader,我的不可编译尝试,

fn read_lines<P, I: IntoIterator<Item = P>>(files: I) -> Result<io::Lines<io::BufReader<File>>>
where
    P: AsRef<Path>,
{
    let handles = files.into_iter()
    .map(|path| 
             File::open(path).unwrap());

    // I guess it is hard (impossible?) to define the type of this reduction,
    //    Chain<File, Chain<File, ..., Chain<File, File>>>
    // and that is the reason the compiler is complaining.
    match handles.reduce(|a, b| a.chain(b)) {
    Some(combination) => Ok(BufReader::new(combination).lines()),
    None => {
        // Not nice, hard fail if the array len is 0
        Ok(BufReader::new(handles.next().unwrap()).lines())
    },
    }
}

这给出了一个预期的错误,我不确定如何解决,

error[E0599]: the method `chain` exists for struct `File`, but its trait bounds were not satisfied
   --> src/bin.rs:136:35
    |
136 |     match handles.reduce(|a, b| a.chain(b)) {
    |                                   ^^^^^ method cannot be called on `File` due to unsatisfied trait bounds
    | 
   ::: /home/test/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/fs.rs:91:1
    |
91  | pub struct File {
    | --------------- doesn't satisfy `File: Iterator`
    | 
   ::: /home/test/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:902:8
    |
902 |     fn chain<R: Read>(self, next: R) -> Chain<Self, R>
    |        ----- the method is available for `Box<File>` here
    |
    = note: the following trait bounds were not satisfied:
            `File: Iterator`
            which is required by `&mut File: Iterator`
    = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
    |
1   | use std::io::Read;
    |

error: aborting due to previous error

我尝试用 Box 扭曲代码没有成功,但似乎根本问题是这种减少的类型是“未定义的”:Chain<File, Chain<File, ..., Chain<File, File>>>国际大学联合会。 Rustacean 如何定义这样的方法?是否可以不使用动态“框”?

最佳答案

I guess it is hard (impossible?) to define the type of this reduction, Chain<File, Chain<File, ..., Chain<File, File>>>. [...] How would a Rustacean define a method like this?

您要查找的组合子是 flat_map :

let handles = files.into_iter().map(|path| File::open(path).unwrap());
handles.flat_map(|handle| BufReader::new(handle).lines())

此外,您的返回类型是不必要的特定, promise 对句柄上的迭代器和来自句柄的行上的迭代器的特定实现。即使你让它工作,你的函数的签名将与它的实现紧密耦合,这意味着你将无法例如在不对 API 进行重大更改的情况下切换到更高效的方法。

为避免这种耦合,您可以使用 impl Trait 返回类型。这样你的函数的签名只 promise 返回值的类型将实现 Iterator .该函数可能如下所示:

fn read_lines<P, I: IntoIterator<Item = P>>(files: I) -> impl Iterator<Item = io::Result<String>>
where
    P: AsRef<Path>,
{
    let handles = files.into_iter().map(|path| File::open(path).unwrap());
    handles.flat_map(|handle| BufReader::new(handle).lines())
}

最后,如果你真的想合并reducechain , 你也能做到。您需要使用 Box 的直觉是正确的,但使用起来要容易得多 fold() reduce() :

handles.fold(
    Box::new(std::iter::empty()) as Box<dyn Iterator<Item = _>>,
    |iter, handle| Box::new(iter.chain(BufReader::new(handle).lines())),
)

折叠从一个空的迭代器开始,装箱并强制转换为一个特征对象,然后是每个 handle 的链线。到前一个迭代器链的末尾。链的每个结果都被装箱,以便其类型被删除为 Box<dyn Iterator<Item = io::Result<String>>> ,这消除了类型级别的递归。函数的返回类型可以是 impl IteratorBox<dyn Iterator> ,两者都会编译。

请注意,这种解决方案效率低下,不仅是因为装箱,还因为最终迭代器将包装所有以前的迭代器。虽然从删除的类型中看不到递归,但它存在于实现中,最终的 next()内部必须遍历所有堆叠的迭代器,如果有足够数量的files,甚至可能会炸毁堆栈。 .基于flat_map()的解决方案没有这个问题。

关于rust - 如何减少 std::io::Chain,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67382232/

相关文章:

rust - 如何通过将货币转移到另一个帐户而不是使用储备货币来修改昵称托盘(在基板中)以设置昵称?

compiler-errors - 为什么将同一文件中的相同结构包含到多个包装箱中会导致类型不匹配错误?

rust - 枚举和结构上的可编码特征边界问题

rust - Ref::map 内部符合人体工学的选项处理

rust - 有没有办法将多个非复制元素移出数组?

compiler-errors - 如何添加外部包并在 Rust 编译器中运行?

process - 如何在 Rust 中存储进程

rust - `method ` sub ` has an incompatible type for trait`(可以为不同的结构实现sub吗?)

rust - 如何指向crates.io中的crate的.crate文件的本地副本?

rust - 在 Rust 中访问属性后调用 &mut self 上的方法