【发布时间】:2018-02-02 05:53:21
【问题描述】:
目标是写一个函数,获取两个路径input_dir和output_dir,并将input_dir中的所有markdown文件转换为output_dir中的html文件。
我终于设法让它运行,但它相当令人沮丧。应该很难的部分非常简单:从 Markdown 到 HTML 的实际转换实际上只有一行。看似简单的部分是我花费时间最长的部分。使用路径向量并将所有文件放入其中是我用glob crate 替换的东西。不是因为我无法让它工作,而是if let 和unwrap 的混乱。一个简单的函数,它遍历元素列表并找出其中哪些实际上是文件而不是目录?如果if let 我需要四个缩进级别,或者我对matches 感到害怕。
我做错了什么?
但是让我们从一些事情开始,我试图在过滤后仅包含实际文件的目录中获取项目列表:
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()));
}
因此,此代码无法构建,因为第 10 行中的文件名的寿命不够长。我想我可以以某种方式创建一个拥有的String,但这会引入另一个嵌套级别,因为OsStr.to_string() 返回一个Result。
现在我查看了glob crate 的代码,他们只是使用了一个可变向量:
fn list_files (path: &str) -> Result<Vec<&str>, &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 Some(name) = entry.file_name().to_str() {
list.push(name)
}
}
}
}
}
Ok(list)
} else {
Err("nope")
}
}
这不仅会增加疯狂的嵌套,还会因同样的问题而失败。如果我从Vec<&str> 更改为Vec<String>,它可以工作:
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 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")
}
}
至少短一点……但它无法编译,因为无法从std::option::Option<std::string::String> 类型元素的迭代器上构建std::vec::Vec<std::string::String> 类型的集合。
在这里很难保持耐心。为什么.filter_map 返回Options 而不是仅仅使用它们进行过滤?现在我必须将第 15 行从 }).collect()) 更改为 }).map(|e| e.unwrap()).collect()),这会再次遍历结果集。
这不可能!
【问题讨论】:
-
ok()返回Option,然后将其包装成Some。你最终得到Option<Option<...>>。从Some(entry.file_name().into_string().ok())中删除Some(...)。这不是一个完整的答案,但至少它可以让你继续前进。 -
如果您的代码有效(我发现很难从阅读您的问题中分辨出来),那么寻求更好的编写方法是better suited for Code Review。
-
谢谢@Shepmaster,但我的问题不是关于那段代码,我只是作为一个例子写的。相反,我想看看我的一般问题是什么导致这段代码如此疯狂地嵌套。
标签: rust ownership borrow-checker