Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Rust中,如何减少代码的重复性?_Rust_Ownership_Borrow Checker - Fatal编程技术网

在Rust中,如何减少代码的重复性?

在Rust中,如何减少代码的重复性?,rust,ownership,borrow-checker,Rust,Ownership,Borrow Checker,目标是编写一个函数,该函数获取两条路径,input\u dir和output\u dir,并将input\u dir中的所有标记文件转换为output\u dir中的html文件 我最终设法让它运行起来,但它相当令人沮丧。应该很难的部分非常简单:从Markdown到HTML的实际转换实际上只有一行。看似简单的部分花费了我最长的时间。我用glob板条箱代替了使用路径向量并将所有文件放入其中。并不是因为我无法让它工作,而是因为如果让它和展开,它会变得一团糟。一个简单的函数,它遍历元素列表并找出其中哪

目标是编写一个函数,该函数获取两条路径,
input\u dir
output\u dir
,并将
input\u dir
中的所有标记文件转换为
output\u dir
中的html文件

我最终设法让它运行起来,但它相当令人沮丧。应该很难的部分非常简单:从Markdown到HTML的实际转换实际上只有一行。看似简单的部分花费了我最长的时间。我用
glob
板条箱代替了使用路径向量并将所有文件放入其中。并不是因为我无法让它工作,而是因为如果让它和
展开
,它会变得一团糟。一个简单的函数,它遍历元素列表并找出其中哪些元素实际上是文件而不是目录?要么我需要四个缩进级别,如果
if let
,要么我对
match
es感到抓狂

我做错了什么

但是,让我们从一些事情开始,我尝试在一个目录中获得一个项目列表,该列表经过筛选,仅包含实际文件:

use std::fs;
use std::vec::Vec;


fn list_files (path: &str) -> Result<Vec<&str>, &str> {
    if let Ok(dir_list) = fs::read_dir(path) {
        Ok(dir_list.filter_map(|e| {
            match e {
                Ok(entry) => match entry.file_type() {
                    Ok(_) => entry.file_name().to_str(),
                    _ => None
                },
                _ => None
            }
        }).collect())
    } else {
        Err("nope")
    }
}


fn main() {
    let files = list_files("testdir");
    println!("{:?}", files.unwrap_or(Vec::new()));
}
这不仅增加了疯狂的嵌套,还导致了同样的问题。如果我从
Vec
更改为
Vec
,它会工作:

fn list_files (path: &str) -> Result<Vec<String>, &str> {
    let mut list = Vec::new();

    if let Ok(dir_list) = fs::read_dir(path) {
        for entry in dir_list {
            if let Ok(entry) = entry {
                if let Ok(file_type) = entry.file_type() {
                    if file_type.is_file() {
                        if let Ok(name) = entry.file_name().into_string() {
                            list.push(name)
                        }
                    }
                }
            }
        }

        Ok(list)
    } else {
        Err("nope")
    }
}
fn列表_文件(路径:&str)->结果{
让mut list=Vec::new();
如果让Ok(目录列表)=fs::读取目录(路径){
用于目录列表中的条目{
如果让Ok(输入)=输入{
如果让Ok(文件类型)=entry.file类型(){
如果文件\u type.is\u file(){
如果让Ok(name)=将.file_name()输入到_string()中{
列表。推送(名称)
}
}
}
}
}
Ok(列表)
}否则{
呃(“不”)
}
}
看来我应该把这个应用到我的第一次尝试中,对吗

fn list_files (path: &str) -> Result<Vec<String>, &str> {
    if let Ok(dir_list) = fs::read_dir(path) {
        Ok(dir_list.filter_map(|e| {
            match e {
                Ok(entry) => match entry.file_type() {
                    Ok(_) => Some(entry.file_name().into_string().ok()),
                    _ => None
                },
                _ => None
            }
        }).collect())
    } else {
        Err("nope")
    }
}
fn列表_文件(路径:&str)->结果{
如果让Ok(目录列表)=fs::读取目录(路径){
Ok(目录列表。过滤器映射(| e |{
匹配e{
确定(条目)=>匹配条目。文件类型(){
Ok()=>Some(entry.file_name().into_string().Ok()),
_=>无
},
_=>无
}
}).collect())
}否则{
呃(“不”)
}
}
至少要短一点…但它无法编译,因为不能从类型为
std::option::option
的元素上的迭代器构建类型为
std::vec::vec
的集合

在这里很难保持耐心。为什么
.filter\u map
会返回
选项
s,而不只是使用它们进行过滤?现在我必须将第15行从
}.collect())
更改为
}.map(|e | e.unwrap()).collect())
,它将在结果集上再次迭代

那不可能是对的

您可以大量依赖:


使用迭代器的替代答案

使用std::fs;
使用std::error::error;
使用std::path::PathBuf;
fn list_文件(路径:&str)->结果{
设x=fs::read_dir(路径)?
.filter_map(| e | e.ok())
.filter(| e | e.metadata().是否正常())
.filter(| e | e.metadata().unwrap().is_file())
.map(| e | e.path())
.收集();
Ok(x)
}
fn main(){
让path=“.”;
对于列表_文件(路径)中的res.unwrap(){
println!(“{:?}”,res);
}
}

ok()
返回
选项
,然后将其包装成
部分
。您将使用
选项
。将
Some(…)
Some(entry.file\u name().中删除到\u string().ok())
。这不是一个完整的答案,但至少可以让你继续。如果你的代码有效(我发现阅读你的问题很难判断),那么寻求一种更好的方法就是。谢谢@Shepmaster,但我的问题不是关于这段代码,我只是作为一个例子写的。相反,我想看看我的一般问题是什么,导致这段代码如此疯狂嵌套。非常感谢@Boiethios!我以前见过那个操作符,但每次我想使用它时,它对我都不起作用。可能是因为它以前只有夜间版本。@koehr更新了!谢谢Rust编译器在优化这些方面有多好?因为我不想一次又一次地迭代。这是迭代器的美妙之处,这只是一次迭代,因为只有一次调用来收集。这个答案很好!然而,OP的代码有一个区别,因为在代码中,第一次出错时不会返回。你只是默默地丢弃它们。
fn list_files (path: &str) -> Result<Vec<String>, &str> {
    if let Ok(dir_list) = fs::read_dir(path) {
        Ok(dir_list.filter_map(|e| {
            match e {
                Ok(entry) => match entry.file_type() {
                    Ok(_) => Some(entry.file_name().into_string().ok()),
                    _ => None
                },
                _ => None
            }
        }).collect())
    } else {
        Err("nope")
    }
}
use std::fs;
use std::io::{Error, ErrorKind};

fn list_files(path: &str) -> Result<Vec<String>, Error> {
    let mut list = Vec::new();

    for entry in fs::read_dir(path)? {
        let entry = entry?;
        if entry.file_type()?.is_file() {
            list.push(entry.file_name().into_string().map_err(|_| {
                Error::new(ErrorKind::InvalidData, "Cannot convert file name")
            })?)
        }
    }

    Ok(list)
}
use std::fs;
use std::io::{Error, ErrorKind};

trait CustomGetFileName {
    fn get_file_name(self) -> Result<String, Error>;
}

impl CustomGetFileName for std::fs::DirEntry {
    fn get_file_name(self) -> Result<String, Error> {
        Ok(self.file_name().into_string().map_err(|_|
            Error::new(ErrorKind::InvalidData, "Cannot convert file name")
        )?)
    }
}

fn list_files(path: &str) -> Result<Vec<String>, Error> {
    let mut list = Vec::new();

    for entry in fs::read_dir(path)? {
        let entry = entry?;
        if entry.file_type()?.is_file() {
            list.push(entry.get_file_name()?)
        }
    }

    Ok(list)
}
use std::fs;
use std::error::Error;
use std::path::PathBuf;

fn list_files(path: &str) -> Result<Vec<PathBuf>, Box<Error>> {
    let x = fs::read_dir(path)?
        .filter_map(|e| e.ok())
        .filter(|e| e.metadata().is_ok())
        .filter(|e| e.metadata().unwrap().is_file())
        .map(|e| e.path())
        .collect();

    Ok(x)
}

fn main() {
    let path = ".";
    for res in list_files(path).unwrap() {
        println!("{:#?}", res);
    }
}