I have code using a nested Result
like this:
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 composing way to reduce this? Something like Haskell or Scala programmers do:
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 :)
}
The answer to your direct question is the questionmark operator which would allow you to replace your whole match
block with
Ok((
t[0].parse::<u8>()?,
t[1].parse::<u8>()?,
t[2].parse::<u8>()?,
t[3].parse::<u8>()?,
))
where essentially ?
will return the error immediately if one is encountered.
That said, Rust already provides APIs for parsing IP addresses. Even if you wanted to maintain your tuple approach (though why would you), 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 pass around the Ipv4Addr
value directly.
Though, I do not see anything bad in @loganfsmyth's answer, I want to add another solution.
Your problem is a very simple and general problem of all programming languages which can be solved very easily if you would have enough time or practice in optimizing solutions. There is some divide and conquer recursive technique which is usually used to solve such problems. For a start, imagine a more simple thing: parsing a single octet from a string. This is a simple parse which you already know. Then mentally try to expand this problem to a larger one - parsing all octets which is a simple repeating process of the smallest problem we have solved earlier (parsing a single octet). This leads us to an iterative/recursive process: do something until. Keeping this in mind I have rewritten your function to a simple iterative process which uses tail-recursion which will not cause a stack overflow as a usual recursion due to it's form:
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"));
}
Keep in mind that Rust
language is a bit more functional than you might think.
Also, I would recommend you to read this book which greatly explains the solution.
You can use early returns to prevent the nesting (but not the repetition).
Note the body of the Err
arms of the matches:
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 the others have said, Rust already has a built-in way to parse IP addresses.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With