rust 是否有一个快捷方式来展开或继续循环?

lmvvr0a8  于 2022-12-04  发布在  其他
关注(0)|答案(6)|浏览(166)

请考虑以下内容:

loop {
    let data = match something() {
        Err(err) => {
            warn!("An error: {}; skipped.", err);
            continue;
        },
        Ok(x) => x
    };

    let data2 = match something_else() {
        Err(err) => {
            warn!("An error: {}; skipped.", err);
            continue;
        },
        Ok(x) => x
    };

    // and so on
}

如果我不需要给data赋ok值,我会使用if let Err(err) = something(),但是有没有一个捷径可以避免复制粘贴Err/Ok分支到上面的代码中,我想,典型的情况是这样的?类似于if let,它也会返回ok值。

swvgeqrz

swvgeqrz1#

虽然我认为E_net4的答案可能是最好的答案,但我还是添加了一个宏,以备将来创建一个单独的函数,并使用?操作符提前返回。
下面是一个简单的skip_fail!宏,当传递一个错误时,continue将成为一个包含循环:

macro_rules! skip_fail {
    ($res:expr) => {
        match $res {
            Ok(val) => val,
            Err(e) => {
                warn!("An error: {}; skipped.", e);
                continue;
            }
        }
    };
}

此宏可用作let ok_value = skip_fail!(do_something());
Playground link which uses skip_fail to print out numbers divisible by 1, 2, and 3, and print an error when one of the divisions would truncate.
同样,我相信在一个单独的函数中使用?,如果没有失败,则返回Ok(end_result),这可能是最惯用的解决方案,所以如果可以使用该答案,您可能应该这样做。

noj0wjuj

noj0wjuj2#

如果你经常要对结果进行“展开或继续”,可以考虑将这个逻辑封装在一个单独的函数中。这样,你就可以利用?语法来引发函数的错误。循环的流逻辑可以写在一个单独的地方(尽管此时你可能不再需要continue)。

loop {
    if let Err(err) = do_event() {
        warn!("An error: {}; skipped.", err);
        // continue; // you also don't need this
    }
}

fn do_event() -> Result<(), YourErrorType> {
    let data = do_something()?; // 
    let x = something_more()?;  // error propagation!
    Ok(())
}
pieyvz9o

pieyvz9o3#

如果必须将多个Ok链接在一起,需要在下一个操作中使用一个Ok的值,并且不关心错误在链中的 * 何处 * 出现,请考虑and_then

loop {
    let outcome = something()
                  .and_then(|a| something_else(a))
                  .and_then(|a| another_thing(a))
                  .and_then(|a| {
                      let b = a + salt;
                      one_more(b)
                  });
    if let Err(e) = outcome {
        warn!("An error: {}; skipped.", e);
    }
}

其中,somethingsomething_elseanother_thingone_more都返回某种形式的Result。尽管本示例删除了continue语句,但当Result的类型为Err时,and_then通过短路有效地模拟了该语句。该行后面的所有调用都将被跳过。
通过在只需要一次函数调用的语句中使用非闭包,可以使这一点更加简洁:

loop {
    let outcome = something()
                  .and_then(something_else)
                  .and_then(another_thing)
                  .and_then(|a| one_more(a + salt));
    if let Err(e) = outcome {
        warn!("An error: {}; skipped.", e);
    }
}

(Note函数上缺少括号,这表明它们被用作可调用对象,而不是获取其返回值)

5w9g7ksd

5w9g7ksd4#

如果您愿意使用不稳定的特性,可以使用try块:

#![feature(try_blocks)]

pub fn something() -> Result<String, String> {
    Err(String::from("Badness"))
}

pub fn something_else() -> Result<String, String> {
    Ok(String::from("Ok"))
}

pub fn main() {
    loop {
        let result: Result<(), String> = try {
            let data = something()?;
            let data2 = something_else()?;
        };
        if let Err(e) = result {
            println!("An error: {}; skipped.", e)
        }
    }
}

正如shepmaster在注解中提到的,这可以通过使用一个立即求值的闭包(一个立即调用的函数表达式,简称IIFE)来完成,而没有任何不稳定的特性。这是MutantOctopus在解决方案注解中对E_net4提出的解决方案的修改。

pub fn something() -> Result<String, String> {
    Err(String::from("Badness"))
}

pub fn something_else() -> Result<String, String> {
    Ok(String::from("Ok"))
}

pub fn main() {
    loop {
        let result: Result<(), String> = (|| {
            let data = something()?;
            let data2 = something_else()?;
            Ok(())
        })();
        if let Err(e) = result {
            println!("An error: {}; skipped.", e)
        }
    }
}
ruarlubt

ruarlubt5#

Rust 1.65.0添加了let-else语句.所以你可以这样写:

loop {
        let data = something()
        let Ok(ok_data) = data else {
            warn!("skipped.");
            continue;
        };
        // ok_data is available here
        let Ok(data2) = something_else(ok_data) else {
            continue;
        };
        // and so on
    }

但您将无法访问err变量

58wvjzkj

58wvjzkj6#

您可以使用我的unwrap_or板条箱来完成此操作。
您可以使用它来生成漂亮且干净的代码:

unwrap_or_ok!(callable(&mut param), _, return);

loop {
    let data = unwrap_ok_or!(something(), err, {
        warn!("An error: {}; skipped.", err);
        continue;
    });

    let data2 = unwrap_ok_or!(somethingElse(), err, {
        warn!("An error: {}; skipped.", err);
        continue;
    });
}

相关问题