如何减少Rust中的样板嵌套结果

o0o*_*o0o 5 rust

我有使用嵌套的代码,Result如下所示:

fn ip4(s: &str) -> Result<(u8, u8, u8, u8), num::ParseIntError> {
    let t: Vec<_> = s.split('.').collect();
    match t[0].parse::<u8>() {
        Ok(a1) => {
            match t[1].parse::<u8>() {
                Ok(a2) => {
                    match t[2].parse::<u8>() {
                        Ok(a3) => {
                            match t[3].parse::<u8>() {
                                Ok(a4) => {
                                    Ok((a1, a2, a3, a4))
                                }
                                Err(er) => Err(er)
                            }
                        },
                        Err(er) => Err(er)
                    }
                }
                Err(er) => Err(er)
            }
        }
        Err(er) => Err(er),
    }
}
Run Code Online (Sandbox Code Playgroud)

是否有任何功能或组成方式来减少这种情况?像Haskell或Scala程序员那样:

fn ip4(s: &str) -> Result<(u8, u8, u8, u8), num::ParseIntError> {
  let t: Vec<_> = s.split('.').collect();
  Result
  .lift((,,,))
  .ap(() -> t[0].parse::<u8>())
  .ap(() -> t[1].parse::<u8>())
  .ap(() -> t[2].parse::<u8>())
  .ap(() -> t[3].parse::<u8>()) // maybe more concise in Haskell or Scala but I think it's enough :)
}
Run Code Online (Sandbox Code Playgroud)

log*_*yth 11

您的直接问题的答案是questionmark运算符,它允许您替换整个match

Ok((
    t[0].parse::<u8>()?,
    t[1].parse::<u8>()?,
    t[2].parse::<u8>()?,
    t[3].parse::<u8>()?,
))
Run Code Online (Sandbox Code Playgroud)

?如果遇到错误,将立即返回错误.

也就是说,Rust已经提供了用于解析IP地址的API.即使你想保持你的元组方法(虽然你为什么会这样),你可以实现你的功能

fn ip4(s: &str) -> Result<(u8, u8, u8, u8), net::AddrParseError> {
    let addr: net::Ipv4Addr = s.parse()?;
    let octets = addr.octets();
    Ok((octets[0], octets[1], octets[2], octets[3]))
}
Run Code Online (Sandbox Code Playgroud)

或者直接传递Ipv4Addr值.