我正在编写一个将在无限循环中调用的函数,并且仅在从Web服务获取格式良好的数据时执行某些操作。如果服务已关闭,返回非json,或返回我们不理解的json,该函数应该只记录错误并返回(暂停后再次调用)。
我发现自己正在复制和粘贴这样的东西:
let v = match v {
Ok(data) => data,
Err(error) => {
println!("Error decoding json: {:?}", error);
return;
}
};
错误匹配器的主体每次都会有所不同。有时它会引起恐慌,有时它会有不同的信息,有时error
的元素可以进一步细分以形成更好的信息,但构造的其余部分将是相同的。
这是否有简写?我知道?
syntax,但是传播的是if (v == null)
{
Console.WriteLine("Error decoding json!");
return;
}
{{3}}。我不觉得传播会对场景有所帮助,因为在上述场景中出现错误时需要稍微不同的处理。这是因为处理中的特定差异属于这里,而不是堆栈。
我还没有在Rust写过很多代码,所以我很可能错过了一些明显的东西。
在C#中,上面的内容如下所示:
if (error != null)
{
Console.WriteLine($"Error decoding json: {error}");
return;
}
或
if let Err(error) = v {
println!("Error decoding json: {:?}", error);
return;
}
let v = v.unwrap();
这两者都比Rust更简洁。
如果我理解下面的评论,缩短的一种方法就是这样:
SOME_URL/{screenSize}/{type}
这看起来更紧凑,谢谢。这是惯用的吗?你会这样写吗?
答案 0 :(得分:3)
作为自定义macro_rule
的替代方法,您还可以使用?
Option<T>
和Result
的特征扩展来打印错误并转换成功值。
pub trait ResultOkPrintErrExt<T> {
fn ok_or_print_err(self, msg: &str) -> Option<T>;
}
impl<T, E> ResultOkPrintErrExt<T> for Result<T, E>
where
E: ::std::fmt::Debug,
{
fn ok_or_print_err(self, msg: &str) -> Option<T> {
match self {
Ok(v) => Some(v),
Err(e) => {
eprintln!("{}: {:?}", msg, e);
None
}
}
}
}
fn read_input() -> Result<u32, ()> {
// Ok(5)
Err(())
}
fn run() -> Option<()> {
let v: u32 = read_input().ok_or_print_err("invalid input")?;
println!("got input: {}", v);
Some(())
}
fn main() {
run();
}
答案 1 :(得分:3)
如果在上述情况中出现错误时需要稍微不同的处理,我不认为传播会对场景有所帮助。这是因为处理中的特定差异属于这里,而不是堆栈。
这是自定义错误类型可以帮助的东西。在这种情况下,您有一个共同的行为(“记录错误”),并且您希望以不同的方式为不同的值执行此操作。将“记录错误”部分移动到调用者(让我们调用函数try_poll
)是有意义的:
loop {
if let Err(e) = try_poll() {
println!("{}", e);
}
sleep(100);
}
为每种错误类型Display
创建一个实现From<E>
和E
的类型:
enum PollError {
NetworkError(NetworkError),
JsonParseError(JsonParseError),
}
impl fmt::Display for PollError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
PollError::NetworkError(ref e) => write!(f, "Error downloading file: {:?}", e),
PollError::JsonParseError(ref e) => write!(f, "Error parsing JSON: {:?}", e),
}
}
}
impl From<NetworkError> for PollError {
fn from(e: NetworkError) -> Self {
PollError::NetworkError(e)
}
}
impl From<JsonParseError> for PollError {
fn from(e: JsonParseError) -> Self {
PollError::JsonParseError(e)
}
}
现在您可以使用?
传播错误,但调用者仍然不必关心具体错误。
fn try_poll() -> Result<(), PollError> {
let data = try_fetch_content()?;
let json = try_parse_json(data)?;
println!("Parsed {:?}", json);
Ok(())
}
From
实现。关于这个的繁琐部分是所有impl From
s,由于自定义错误类型,这是必需的。如果唯一能用错误完成的事情是log并忽略它,那么自定义错误类型并不是特别有用 - 唯一真正需要返回的是错误消息本身。
在这种情况下,让try_poll
代替Result<(), String>
,并使用Result::map_err
将每个错误立即转换为错误消息,然后再使用?
传播它:
fn try_poll() -> Result<(), String> {
let data = try_fetch_content()
.map_err(|e| format!("Error downloading file: {:?}", e))?;
let json = try_parse_json(data)
.map_err(|e| format!("Error parsing JSON: {:?}", e))?;
println!("Parsed {:?}", json);
Ok(())
}
first edition of The Rust Programming Language可以将String
作为错误类型说明:
经验法则是定义您自己的错误类型,但是
String
错误类型会在紧要关头进行,特别是在您编写应用程序时。如果您正在编写库,则应首选定义您自己的错误类型,这样您就不会不必要地从调用者中删除选项。