是否有展开或继续循环的快捷方式?

Is there a shortcut to unwrap or continue in a loop?

考虑一下:

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
}

如果我不需要将 ok 值分配给 data,我会使用 if let Err(err) = something(),但是上面的代码是否有一个快捷方式可以避免复制粘贴我认为 Err/Ok 分支是典型的情况吗?像 if let 这样的东西也会 return 好的值。

如果您要经常 "unwrap or continue" 结果,请考虑将该逻辑封装在一个单独的函数中。有了它,您可以利用 ? 语法从函数中引发错误。然后可以将循环的流逻辑写在一个地方(尽管此时,您可能不再需要 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(())
}

如果必须把多个Ok链接在一起,需要在下一次运算中使用一个Ok的值,不用关心where 在链中出现错误,考虑 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 都是 return Result 的某种形式。尽管此示例删除了 continue 语句,但当 Result 的类型为 Err 时,and_then 通过 short-circuiting 有效地模拟了它。所有后续呼叫都将被跳过。

您可以通过在只需要一个函数调用的语句上使用 non-closures 来使其更加简洁:

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);
    }
}

(请注意函数上缺少括号,这表明它们被用作可调用对象而不是采用它们的 return 值)

虽然我认为 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),这可能是最惯用的解决方案,所以如果你可以使用那个答案,你可能应该.

如果您愿意使用不稳定的功能,您可以为此使用 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)
        }
    }
}

你可以使用我的 unwrap_or crate 来完成这个。

您可以使用它来编写漂亮干净的代码:

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;
    });
}