简体   繁体   English

如何减少 std::io::Chain

[英]How to reduce std::io::Chain

Moving on from https://doc.rust-lang.org/rust-by-example/std_misc/file/read_lines.html , I would like to define a function that accepts an iterable of Paths, and returns a Reader that wraps all the paths into a single stream, my non-compilable attempt, Moving on from https://doc.rust-lang.org/rust-by-example/std_misc/file/read_lines.html , I would like to define a function that accepts an iterable of Paths, and returns a Reader that wraps all进入单个 stream 的路径,我的不可编译尝试,

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())
    },
    }
}

This gives an expected error, which I am unsure how to address,这给出了一个预期的错误,我不确定如何解决,

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

I've tried contorting the code with Box 's without success, but it seems the fundamental issue is that the type of this reduction is "undefined": Chain<File, Chain<File, ..., Chain<File, File>>> IIUC.我试过用Box扭曲代码但没有成功,但似乎根本问题是这种减少的类型是“未定义”: Chain<File, Chain<File, ..., Chain<File, File>>> IIUC。 How would a Rustacean define a method like this? Rustacean 如何定义这样的方法? Is it possible without using dynamic "boxes"?是否可以不使用动态“框”?

I guess it is hard (impossible?) to define the type of this reduction, Chain<File, Chain<File, ..., Chain<File, File>>> .我想很难(不可能?)定义这种减少的类型Chain<File, Chain<File, ..., Chain<File, File>>> [...] How would a Rustacean define a method like this? [...] Rustacean 如何定义这样的方法?

The combinator you are looking for is flat_map :您正在寻找的组合器是flat_map

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

Also, your return type is unnecessarily specific, committing to a particular implementation of both the iterator over the handles and the iterator over the lines coming from a handle.此外,您的返回类型是不必要的特定,承诺对句柄上的迭代器和来自句柄的行上的迭代器的特定实现。 Even if you get it to work, the signature of your function will be tightly coupled to its implementation, meaning you won't be able to to eg switch to a more efficient approach without introducing a breaking change to the API.即使您让它工作,您的 function 的签名也将与其实现紧密耦合,这意味着您将无法在不对 API 进行重大更改的情况下切换到更高效的方法。

To avoid such coupling, you can use an impl Trait return type.为避免这种耦合,您可以使用impl Trait返回类型。 That way the signature of your function only promises that the type of the returned value will implement Iterator .这样,您的 function 的签名仅承诺返回值的类型将实现Iterator The function could then look like this: function 可能如下所示:

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())
}

Finally, if you really want to combine reduce and chain , you can do that too.最后,如果你真的想结合reducechain ,你也可以这样做。 Your intuition that you need to use a Box is correct, but it is much easier to use fold() than reduce() :您需要使用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())),
)

Folding starts with an empty iterator, boxed and cast to a trait object, and proceeds to chain lines of each handle to the end of the previous iterator chain.折叠从一个空的迭代器开始,装箱并转换为特征 object,然后将每个handle的链线连接到前一个迭代器链的末尾。 Each result of the chain is boxed so that its type is erased to Box<dyn Iterator<Item = io::Result<String>>> , which eliminates the recursion on the type level.链的每个结果都被装箱,以便将其类型擦除为Box<dyn Iterator<Item = io::Result<String>>> ,从而消除了类型级别的递归。 The return type of the function can be either impl Iterator or Box<dyn Iterator> , both will compile. function 的返回类型可以是impl IteratorBox<dyn Iterator> ,两者都会编译。

Note that this solution is inefficient, not just due to boxing, but also because the final iterator will wrap all the previous ones.请注意,此解决方案效率低下,不仅是由于装箱,还因为最终迭代器将包装所有先前的迭代器。 Although the recursion is not visible from the erased types, it's there in the implementation, and the final next() will internally have to go through all the stacked iterators, possibly even blowing up the stack if there is a sufficient number of files .尽管从擦除的类型中看不到递归,但它存在于实现中,并且最终的next()将在内部必须通过所有堆叠的迭代器 go ,如果有足够数量的files ,甚至可能会炸毁堆栈。 The solution based on flat_map() doesn't have that issue.基于flat_map()的解决方案没有这个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM