如何解决使用处理错误并生成 Result<...>
作为一项学习练习,我正在尝试编写一个函数,该函数在文件树上实现迭代器,使用函数式风格(使用 flat_map
之类的函数组合其他迭代器和东西),它产生类型 Result,并使用它来表示错误(而不是恐慌)。这在 Python 和 Kotlin 等其他语言中很容易,但我听说在 Rust 中更难,我想知道它有多难。我有一个简化的变体工作(它会因错误而恐慌),但我无法找出正确处理错误的方法来编写相同的函数。在下面的代码中,在我应该产生错误的 match
的分支中,我已经尝试了所有可以想象的(对我来说)并且我总是遇到关于 match
臂中不兼容类型的编译时错误。我在这里想念什么?
use std::path::{Path,PathBuf};
use std::fs;
use std::iter::empty;
use std::iter::once;
use std::error::Error;
type Result<T> = std::result::Result<T,Box<dyn Error>>;
// simpler version which works,but panics on errors
pub fn subdirectories_recursive_panicking(search_root_path: &Path) -> Box<dyn Iterator<Item=PathBuf>> {
if !search_root_path.is_dir() {
return Box::new(empty());
}
let entries = fs::read_dir(search_root_path).unwrap();
Box::new(entries.flat_map(|entry| {
let this_path = entry.unwrap().path();
let nested_paths = subdirectories_recursive_panicking(&this_path);
nested_paths.chain(once(this_path))
}))
}
// below is the unlucky attempt to make it correctly handle errors
// (can't figure out why I get incompatible types in match arms)
// it works,however,if I put `unreachable!()` in place of error expressions,suggesting that the
// other code makes sense,but that would obviously panic on errors
pub fn subdirectories_recursive(search_root_path: &Path) -> Box<dyn Iterator<Item=Result<PathBuf>>> {
if !search_root_path.is_dir() {
return Box::new(empty());
}
let entries_result = fs::read_dir(search_root_path);
match entries_result {
Ok(entries) => {
Box::new(entries.flat_map(|e| {
match e {
Ok(entry) => {
let this_path = entry.path();
let nested_paths = subdirectories_recursive(&this_path);
Box::new(nested_paths.chain(once(Ok(this_path))))
}
Err(e) => {
unreachable!()
// the following doesn't compile:
// Box::new(once(Err(Box::new(e))))
}
}
}))
}
Err(e) => {
unreachable!()
// the following doesn't compile:
// Box::new(once(Err(Box::new(e))))
}
}
}
error[E0308]: `match` arms have incompatible types
--> src/lib.rs:44:25
|
36 | / match e {
37 | | Ok(entry) => {
38 | | let this_path = entry.path();
39 | | let nested_paths = subdirectories_recursive(&this_path);
40 | | Box::new(nested_paths.chain(once(Ok(this_path))))
| | ------------------------------------------------- this is found to be of type `Box<std::iter::Chain<Box<dyn Iterator<Item = std::result::Result<PathBuf,Box<(dyn std::error::Error + 'static)>>>>,std::iter::Once<std::result::Result<PathBuf,Box<(dyn std::error::Error + 'static)>>>>>`
... |
44 | | Box::new(once(Err(Box::new(e))))
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::iter::Chain`,found struct `std::iter::Once`
45 | | }
46 | | }
| |_________________- `match` arms have incompatible types
|
= note: expected type `Box<std::iter::Chain<Box<dyn Iterator<Item = std::result::Result<PathBuf,Box<(dyn std::error::Error + 'static)>>>>>`
found struct `Box<std::iter::Once<std::result::Result<_,Box<std::io::Error>>>>`
error[E0271]: type mismatch resolving `<std::iter::Once<std::result::Result<_,Box<std::io::Error>>> as Iterator>::Item == std::result::Result<PathBuf,Box<(dyn std::error::Error + 'static)>>`
--> src/lib.rs:51:13
|
51 | Box::new(once(Err(Box::new(e))))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::error::Error`,found struct `std::io::Error`
|
= note: expected enum `std::result::Result<PathBuf,Box<(dyn std::error::Error + 'static)>>`
found type `std::result::Result<_,Box<std::io::Error>>`
= note: required for the cast to the object type `dyn Iterator<Item = std::result::Result<PathBuf,Box<(dyn std::error::Error + 'static)>>>`
解决方法
在您的外部 match
中,编译器可以推断出由于返回类型的原因,两个分支都应该被强制转换为 Box<dyn Iterator>
。但是该推断不会冒泡到嵌套的 match
中,因为 flat_map
在内部迭代器类型上是通用的。
要解决这个问题,您可以注释匹配的第一个分支,或者创建一个具有预期类型的临时变量:
match e {
Ok(_) => Box::new(...) as Box<dyn Iterator<Item = Result<PathBuf>>>,Err(_) => Box::new(...),}
// or
let iter: Box<dyn Iterator<Item = Result<PathBuf>>> = match e {
Ok(_) => Box::new(...),}
下一组错误源于这样一个事实,即强制仅真正发生在一层深度。 Box<io::Error>
可转换为 Box<dyn Error>
,但 Result<_,Box<io::Error>>
不能转换为 Result<_,Box<dyn Error>>
。
要解决这个问题,您需要主动将其转换为正确的类型:
Box::new(once(Err(Box::new(e) as Box<dyn Error>)))
这是编译版本:
use std::path::{Path,PathBuf};
use std::fs;
use std::iter::empty;
use std::iter::once;
use std::error::Error;
type Result<T> = std::result::Result<T,Box<dyn Error>>;
// simpler version which works,but panics on errors
pub fn subdirectories_recursive_panicking(search_root_path: &Path) -> Box<dyn Iterator<Item=PathBuf>> {
if !search_root_path.is_dir() {
return Box::new(empty());
}
let entries = fs::read_dir(search_root_path).unwrap();
Box::new(entries.flat_map(|entry| {
let this_path = entry.unwrap().path();
let nested_paths = subdirectories_recursive_panicking(&this_path);
nested_paths.chain(once(this_path))
}))
}
// below is the unlucky attempt to make it correctly handle errors
// (can't figure out why I get incompatible types in match arms)
// it works,however,if I put `unreachable!()` in place of error expressions,suggesting that the
// other code makes sense,but that would obviously panic on errors
pub fn subdirectories_recursive(search_root_path: &Path) -> Box<dyn Iterator<Item=Result<PathBuf>>> {
if !search_root_path.is_dir() {
return Box::new(empty());
}
let entries_result = fs::read_dir(search_root_path);
match entries_result {
Ok(entries) => {
Box::new(entries.flat_map(|e| {
let iter: Box<dyn Iterator<Item = Result<PathBuf>>> = match e {
Ok(entry) => {
let this_path = entry.path();
let nested_paths = subdirectories_recursive(&this_path);
Box::new(nested_paths.chain(once(Ok(this_path))))
}
Err(e) => {
// the following doesn't compile:
Box::new(once(Err(Box::new(e) as Box<dyn Error>)))
}
};
iter
}))
}
Err(e) => {
// the following doesn't compile:
Box::new(once(Err(Box::new(e) as Box<dyn Error>)))
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。