How to reduce nested patterns Result in rust

I have code nested Resultas follows:

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

Is there any function or way to create it? Something like Haskell or Scala programmers:

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 enough :)
}
+4
source share
3 answers

The answer to your direct question: a question mark operator that allows you to replace the entire block matchwith

Ok((
    t[0].parse::<u8>()?,
    t[1].parse::<u8>()?,
    t[2].parse::<u8>()?,
    t[3].parse::<u8>()?,
))

where essentially it ?will immediately return an error if it occurs.

However, Rust already provides an API for parsing IP addresses. Even if you want to maintain your tuple (although why do you need it), you could implement your function as

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

or just just pass the value Ipv4Addr.

+10
source

, @loganfsmyth, .

- , , . , . : . , . - , , ( ). / : - . , , , - :

use std::num;

#[derive(Debug, Copy, Clone)]
struct IpAddressOctets(pub u8, pub u8, pub u8, pub u8);
type Result = std::result::Result<IpAddressOctets, num::ParseIntError>;

fn ipv4(s: &str) -> Result {
    let octets_str_array: Vec<_> = s.split('.').collect();

    // If it does not contain 4 octets then there is a error.
    if octets_str_array.len() != 4 {
        return Ok(IpAddressOctets(0, 0, 0, 0))  // or other error type
    }

    let octets = Vec::new();

    fn iter_parse(octets_str_array: Vec<&str>, mut octets: Vec<u8>) -> Result {
        if octets.len() == 4 {
            return Ok(IpAddressOctets(octets[0], octets[1], octets[2], octets[3]))
        }

        let index = octets.len();
        octets.push(octets_str_array[index].parse::<u8>()?);
        iter_parse(octets_str_array, octets)
    }

    iter_parse(octets_str_array, octets)
}

fn main() {
    println!("IP address octets parsed: {:#?}", ipv4("10.0.5.234"));
}

, Rust , .

, , .

+1

You can use early returns to prevent nesting (but not repetition).

Note the body of the Errmatch arrows :

fn ip4(s: &str) -> Result<(u8, u8, u8, u8), num::ParseIntError> {
    let t: Vec<_> = s.split('.').collect();

    let a1 = match t[0].parse::<u8>() {
        Ok(x) => x,
        Err(er) => return Err(er),
    };
    let a2 = match t[1].parse::<u8>() {
        Ok(x) => x,
        Err(er) => return Err(er),
    };
    let a3 = match t[2].parse::<u8>() {
        Ok(x) => x,
        Err(er) => return Err(er),
    };
    let a4 = match t[3].parse::<u8>() {
        Ok(x) => x,
        Err(er) => return Err(er),
    };

    (a1, a2, a3, a4)
}

But, as others have said, Rust already has a built-in way to parse IP addresses.

+1
source

Source: https://habr.com/ru/post/1689357/


All Articles