简体   繁体   English

我可以简化处理Option的Rust代码 <T> 和结果 <T> ?

[英]Can I simplify this Rust code dealing with Option<T> and Result<T>?

I'm working on my first actual Rust program, a custom i3status. 我正在研究我的第一个实际的Rust程序,一个自定义的i3status。 It's coming along nicely, but there's two places where I have to deal with Result<T> and Option<T> and the code looks really ugly, leading me to believe that I've missed some language feature or library function for writing these parts more cleanly. 它很顺利,但有两个地方我必须处理Result<T>Option<T>并且代码看起来非常难看,让我相信我错过了一些语言功能或库函数来编写这些部分更干净。

This is the file in question , but I'll quote the relevant parts here. 这是有问题的文件 ,但我会在这里引用相关部分。 The first one is this: 第一个是:

fn read_number_from_file(path: &str) -> Option<i64> {
    let mut file = match File::open(path) {
        Ok(f) => f,
        Err(_) => return None,
    };
    let mut contents = String::new();
    match file.read_to_string(&mut contents) {
        Ok(_) => {},
        Err(_) => return None,
    };
    match contents.trim().parse::<i64>() {
        Ok(val) => Some(val),
        Err(_) => None,
    }
}

Swallowing error messages here is intentional. 这里吞咽错误信息是故意的。 An i3status does not have an stderr, so all I can do is skip rendering of the broken parts. i3status没有stderr,所以我所能做的就是跳过渲染损坏的部分。

Regardless of that, this code looks ugly, with its repeated use of match to discard Err values. 无论如何,这段代码看起来很丑陋,重复使用match来丢弃Err值。 I tried to use the new ? 我试过用新的? operator by making the return type a std::io::Result<i64> , but str::parse returns a different type of error, so that doesn't work AFAICS. 通过使返回类型为std::io::Result<i64> ,但str::parse返回不同类型的错误,因此std::io::Result<i64>不起作用。 I therefore went with Option<i64> as the lowest common denominator. 因此,我将Option<i64>作为最低标准。

The second ugly part is where this function gets used: 第二个丑陋的部分是使用此函数的地方:

let energy_full = match read_number_from_file(ENERGY_FULL_PATH) {
    Some(val) => val,
    None      => return Vec::new(),
};
let energy_now = match read_number_from_file(ENERGY_NOW_PATH) {
    Some(val) => val,
    None      => return Vec::new(),
};
let is_charging = match read_number_from_file(POWER_ONLINE_PATH) {
    Some(val) => val > 0,
    None      => return Vec::new(),
};

I feel like one should be able to contract each of the match expressions into a function call like .or_else() , but .or_else(|| return Vec::new()) obviously won't work since the return is scoped to the lambda instead of the original function. 我觉得应该能够将每个match表达式收缩到像.or_else()类的函数调用中,但是.or_else(|| return Vec::new())显然不起作用,因为return的作用域是lambda而不是原始函数。

So, in both cases, my question is whether I can replace match by something more compact and idiomatic. 所以,在这两种情况下,我的问题是我是否可以通过更紧凑和惯用的东西来取代match

To combine Option<T> s with Result<T, E> s, use ok() and the question mark operator: 要将Option<T>Result<T, E> s组合,请使用ok()和问号运算符:

fn read_number_from_file(path: &str) -> Option<i64> {
    let mut file = File::open(path).ok()?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).ok()?;
    contents.trim().parse::<i64>().ok()
}

As for the second part of the question, if you can contain all those variable bindings in a single function: 至于问题的第二部分,如果你可以在一个函数中包含所有这些变量绑定:

fn foo() -> Option<Vec<i64>> { // or some other Option<Vec>
    let energy_full = read_number_from_file(ENERGY_FULL_PATH)?;
    let energy_now = read_number_from_file(ENERGY_NOW_PATH)?;
    let is_charging = read_number_from_file(POWER_ONLINE_PATH)? > 0;

    Some(Vec::new()) // placeholder for the valid return value
}

You can then use unwrap_or_else() on its result to return an empty Vec upon any error: 然后,您可以在其结果上使用unwrap_or_else()以在出现任何错误时返回空Vec

let foo = foo().unwrap_or_else(|| vec![]);

Or just unwrap_or() , because empty vectors don't allocate memory : 或者只是unwrap_or() ,因为空向量不分配内存

let foo = foo().unwrap_or(vec![]);

For the second part, you can use one of the comprehension crates available on crates.io like map_for , comp or mdo . 对于第二部分,您可以使用crates.io上提供的其中一个理解包,例如map_forcompmdo For example with map_for : 例如,使用map_for

return map_for!(
    energy_full  <- read_number_from_file(ENERGY_FULL_PATH);
    energy_now   <- read_number_from_file(ENERGY_NOW_PATH);
    power_online <- read_number_from_file(POWER_ONLINE_PATH);
    is_charging  = power_online > 0;
    => vec![ energy_full, energy_now ]
).unwrap_or_else (|| vec![]);

Full disclosure: I am the author of the map_for crate. 完全披露:我是map_for箱子的作者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 Rust 中简化这个可怕的嵌套错误处理代码? - How can I simplify this horribly nested error handling code in Rust? 如何展平 Result 的迭代器<Vec<T> ,E&gt; 返回 Result 的迭代器<T,E>生锈? - How do I flatten an iterator of Result<Vec<T>,E> to return an iterator of Result<T,E> in rust? Rust:为什么我不能在这个库中使用 anyhow::Context? - Rust: Why can't I use anyhow::Context with this library? 接受结果是惯用的 Rust<t, e> 作为 function 参数?</t,> - Is it idiomatic Rust to accept a Result<T, E> as a function argument? 如何使用 rust Try trait with Option NoneError? - How can I use rust Try trait with Option NoneError? 如何转换结果<T, E1>导致<T, E2>问号运算符何时无效? - How can I convert a Result<T, E1> to Result<T, E2> when the question mark operator is ineffective? 我怎样才能简化这个解析方法? - How can I simplify this parsing method? JSP:即使我无法显示,我怎样才能在错误页面上运行代码? - JSP: How can I still get the code on my error page to run, even if I can't display it? 我不能使用 pyinstaller - I can't use pyinstaller 此JavaScript代码没有错误,但无法正常工作。 有看不到的秘密错误吗? - There is no error with this javascript code yet it does not work. Is there a secret error I can't see?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM