委托创建数据结构

巴勃罗姆

我对Rust很陌生。在尝试一些小事情时,我编写了以下代码。它仅扫描文件(作为参数)中的特定字符串(“ Started”),并打印出匹配的行:

use std::os;
use std::io::BufferedReader;
use std::io::File;

fn main() {
    for target in os::args().iter() {
        scan_file(target);
    }
}

fn scan_file(path_str: &String) {
    let path = Path::new(path_str.as_bytes());
    let file = File::open(&path);
    let mut reader = BufferedReader::new(file);

    for line in reader.lines() {
        match line {
            Ok(s) => {
                if s.as_slice().contains("Started ") {
                    print!("{}", s);
                }
            }
            Err(_) => return,
        }
    }
}

我的问题是:如何重构函数scan_file,使其看起来像这样(或足够类似)?:

fn scan_file(path_str: &String) {
    for line in each_line_in_file_with_path(path_str) {
        match line {
            Ok(s) => {
                if s.as_slice().contains("Started ") {
                    print!("{}", s);
                }
            }
            Err(_) => return,
        }
    }
}

在此函数的新版本中,三个变量声明不见了。取而代之的是,该函数each_line_in_file_with_path应该处理所有“将路径转换为行”,并返回迭代器。

我尝试了许多失败的尝试,总是由于变量超出我的需求超出范围的缘故。我了解我所遇到的问题(但我认为),但是在任何地方都找不到如何处理此问题的很好的解释。

弗朗西斯·加涅

无法实现工作each_line_in_file_with_path功能-至少要在不增加一些开销和不安全代码的情况下才能实现

让我们看一下所涉及的值及其类型。首先是path类型Pathposix::Pathwindows::Path)的。这些类型的构造函数接收一个BytesContainer按值,因此他们拥有它的所有权。这里没有问题。

接下来是file类型克隆它接收到的路径,因此这里再次没有问题。IoResult<File>File::open()

接下来是reader类型BufferedReader<IoResult<File>>就像一样Path,的构造BufferedReader函数按值接受参数并拥有所有权。

问题出在哪里reader.lines()此值为type Lines<'r, T: 'r>就像类型签名所暗示的那样,此结构包含一个借用的引用。的签名lines显示了借方与借方之间的关系:

fn lines<'r>(&'r mut self) -> Lines<'r, Self>

我们each_line_in_file_with_path现在如何定义each_line_in_file_with_path无法Lines直接返回您可能尝试过这样编写函数:

fn each_line_in_file_with_path<'a, T>(path: &T) -> Lines<'a, BufferedReader<IoResult<File>>>
        where T: BytesContainer {
    let path = Path::new(path);
    let file = File::open(&path);
    let reader = BufferedReader::new(file);
    reader.lines()
}

这给出了编译错误:

main.rs:46:5: 46:11 error: `reader` does not live long enough
main.rs:46     reader.lines()
               ^~~~~~
main.rs:42:33: 47:2 note: reference must be valid for the lifetime 'a as defined on the block at 42:32...
main.rs:42         where T: BytesContainer {
main.rs:43     let path = Path::new(path);
main.rs:44     let file = File::open(&path);
main.rs:45     let reader = BufferedReader::new(file);
main.rs:46     reader.lines()
main.rs:47 }
main.rs:42:33: 47:2 note: ...but borrowed value is only valid for the block at 42:32
main.rs:42         where T: BytesContainer {
main.rs:43     let path = Path::new(path);
main.rs:44     let file = File::open(&path);
main.rs:45     let reader = BufferedReader::new(file);
main.rs:46     reader.lines()
main.rs:47 }
error: aborting due to previous error

那是因为我们试图返回一个a Lines,该aBufferedReader表示当函数返回时该a不再存在(该aLines将包含一个悬空指针)。

现在,人们可能会想,“我将BufferedReader随同一起返回Lines”。

struct LinesInFileIterator<'a> {
    reader: BufferedReader<IoResult<File>>,
    lines: Lines<'a, BufferedReader<IoResult<File>>>
}

impl<'a> Iterator<IoResult<String>> for LinesInFileIterator<'a> {
    fn next(&mut self) -> Option<IoResult<String>> {
        self.lines.next()
    }
}

fn each_line_in_file_with_path<'a, T>(path: &T) -> LinesInFileIterator<'a>
        where T: BytesContainer {
    let path = Path::new(path);
    let file = File::open(&path);
    let reader = BufferedReader::new(file);
    LinesInFileIterator {
        reader: reader, 
        lines: reader.lines()
    }
}

这也不起作用:

main.rs:46:16: 46:22 error: `reader` does not live long enough
main.rs:46         lines: reader.lines()
                          ^~~~~~
main.rs:40:33: 48:2 note: reference must be valid for the lifetime 'a as defined on the block at 40:32...
main.rs:40         where T: BytesContainer {
main.rs:41     let path = Path::new(path);
main.rs:42     let file = File::open(&path);
main.rs:43     let reader = BufferedReader::new(file);
main.rs:44     LinesInFileIterator {
main.rs:45         reader: reader, 
           ...
main.rs:40:33: 48:2 note: ...but borrowed value is only valid for the block at 40:32
main.rs:40         where T: BytesContainer {
main.rs:41     let path = Path::new(path);
main.rs:42     let file = File::open(&path);
main.rs:43     let reader = BufferedReader::new(file);
main.rs:44     LinesInFileIterator {
main.rs:45         reader: reader, 
           ...
main.rs:46:16: 46:22 error: use of moved value: `reader`
main.rs:46         lines: reader.lines()
                          ^~~~~~
main.rs:45:17: 45:23 note: `reader` moved here because it has type `std::io::buffered::BufferedReader<core::result::Result<std::io::fs::File, std::io::IoError>>`, which is non-copyable
main.rs:45         reader: reader, 
                           ^~~~~~
error: aborting due to 2 previous errors

基本上,我们不能有一个包含借用引用的结构,该引用指向该结构的另一个成员,因为当该结构移动时,该引用将变为无效。

有2个解决方案:

  1. 创建一个BufferedReader从文件路径返回a的函数,并.lines()for循环中对其进行调用

  2. 使一个函数接受一个接受每一行的闭包。

    fn main() {
        for target in os::args().iter() {
            scan_file(target.as_slice());
        }
    }
    
    fn for_each_line_in_file_with_path_do(path: &str, action: |IoResult<String>|) {
        let path = Path::new(path.as_bytes());
        let file = File::open(&path);
        let mut reader = BufferedReader::new(file);
        for line in reader.lines() {
            action(line);
        }
    }
    
    fn scan_file(path_str: &str) {
        for_each_line_in_file_with_path_do(path_str, |line| {
            match line {
                Ok(s) => {
                    if s.as_slice().contains("Started ") {
                        print!("{}", s);
                    }
                }
                Err(_) => return,
            }
        });
    }
    

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章