我试图从文件中读取值以创建一个结构体,但我遇到了一对奇怪的错误。我的代码的超级基本实现:
extern crate itertools;
use itertools::Itertools;
use std::io::{self, prelude::*, BufReader};
use std::fs::{self, File};
// The struct I will unpack into
struct BasicExample {
a: String,
b: String,
c: String,
d: String,
}
impl BasicExample {
pub fn new(a: String, b: String, c: String, d: String} -> Self {
BasicExample {
a, b, c, d
}
}
// I'm expecting that reading from the config file might fail, so
// I want to return a Result that can be unwrapped. Otherwise an Err
// will be returned with contained value being &'static str
pub fn from_config(filename: &str) -> io::Result<Self, &'static str> {
let file = File::open(filename).expect("Could not open file");
// read args into a Vec<String>, consuming file
let args: Vec<String> = read_config(file);
// I transfer ownership away from args here
let params: Option<(String, String, String, String)> = args.drain(0..4).tuples().next();
// Then I want to match and return, I could probably do if-let here
// but I don't have my hands around the base concept yet, so I'll
// leave that for later
match params {
Some((a, b, c, d)) => Ok(BasicExample::new(a, b, c, d)),
_ => Err("Could not read values into struct")
}
}
fn read_config(file: File) -> Vec<String> {
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.collect()
}
}
Run Code Online (Sandbox Code Playgroud)
运行cargo check以确保我没有错过任何东西,我收到以下错误:
error[E0107]: wrong number of type arguments: expected 1, found 2
--> src/lib.rs:37:60
|
37 | pub fn from_config(filename: &str) -> io::Result<Self, &'static str> {
| ^^^^^^^^^^^^ unexpected type argument
error: aborting due to previous error
For more information about this error, try `rustc --explain E0107`.
Run Code Online (Sandbox Code Playgroud)
好像有点奇怪。io::Resultshould take <T, E>,我已经给了它E,所以让我们删除那个类型参数,看看会发生什么:
error[E0308]: mismatched types
--> src/lib.rs:54:22
|
54 | _ => Err("Could not read values into AzureAuthentication struct"),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::io::Error`, found reference
|
= note: expected type `std::io::Error`
found type `&'static str`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
Run Code Online (Sandbox Code Playgroud)
出于某种原因,它真的对E我提供的不满意。我是一个完全的 Rust 初学者,所以也许我只是不确定我在看什么。我在这里做错了什么?该itertools所有权诀窍是借来的(公顷)从这个精彩的回答。
这实际上是一个超级基本的错误,但在您了解(和爱)之前,它看起来很神秘std::io。
简而言之,std::result::Result(你知道的结果) !== std::io::Result。第一个文档在这里,第二个在这里
您会在第二个中注意到它实际上是Result<T, std::io::Error>. 这意味着它实际上是它的简写,其中您的错误案例是std::io::Error.
因此,当您尝试Err()使用字符串切片时,您的代码是不正确的(因为切片std::io::Error显然不是)。
有多种方法可以解决此问题:
into()强制转换)std::io::Error实例这两个选项都有有效的情况,这就是我提到两者的原因。第二个相对容易完成,就像这样(完整路径用于文档目的)。假设您返回一个与未找到实体匹配的错误。你可以这样做:
`Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Could not read values into AzureAuthentication struct"))`
Run Code Online (Sandbox Code Playgroud)
但是,对于您的功能,有一种更好的方法:
pub fn from_config(filename: &str) -> io::Result<Self> {
let file = File::open(filename)?;
let args: Vec<String> = read_config(file); // This has no error possibility
let params: Option<(String, String, String, String)> = args.drain(0..4).tuples().next();
params.ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "Could not read values into struct")).map(|(a, b, c, d)| BasicExample::new(a,b,c,d))
}
Run Code Online (Sandbox Code Playgroud)
这会从您的方法中删除所有间接性,并整齐地将错误类型一一折叠起来,因此您不必担心它们。在Option被变成了Result感谢ok_or,一切都很好在世界上最好的:-)
Rust 中的一个常见模式是,如果您的模块使用了很多Result<T, ModuleSpecificErrorType>,那么您可以进行自定义Result<T>以抽象出错误类型。此自定义类型少了一个通用参数,因为错误类型是硬编码的。
Astd::io::Result<T>是对 的抽象std::result:Result<T, std::io::Error>。
请参阅的文档io::Result。