'How to handle errors that occur inside an initializer macro?

I have this piece of code:


use std::result::Result;

#[derive(Debug)]
pub struct Addr {
    ip: [u8; 4],
    port: u16
}

trait IntoAddr {
    fn  into(self) -> Addr;
}

impl Addr {
    pub fn  new<T>(args: T) -> Addr
    where T: IntoAddr {
        args.into()
    }

    pub fn  ip(&self) -> [u8; 4] {
        self.ip
    }

    pub fn  port(&self) -> u16 {
        self.port
    }
}

impl IntoAddr for ([u8; 4], u16) {
    fn  into(self) -> Addr {
        Addr {
            self.0,
            self.1
        }
    }
}

impl IntoAddr for ([String; 4], u16) {
    fn  into(self) -> Addr {
        let mut i = -1;

        Addr {
            arr![self.1[++i].parse().unwrap(); 4],
            self.1
        }
    }
}

I want to either take a ([u8; 4], u16) or ([String; 4], 16) as parameters of new to initialize my struct Addr, what is the correct way to handle the possible parse error inside arr! macro for the string without unwrapping ? Can we put a match statement inside the macro?



Solution 1:[1]

If the initialization may fail, you must return a Result (or allow panic with .unwrap). Use the ? syntax to immediately return an Err if parsing fails.

    fn try_from(ip: &[&str; 4], port: u16) -> Result<Addr, ParseIntError> {
        Ok(Addr {
            ip: [
                ip[0].parse()?,
                ip[1].parse()?,
                ip[2].parse()?,
                ip[3].parse()?,
            ],
            port,
        })
    }

If you really want to use a macro for this, seq-macro seems to do the job.

use seq_macro::seq;
use std::num::ParseIntError;
use std::result::Result;

#[derive(Debug)]
pub struct Addr {
    ip: [u8; 4],
    port: u16,
}

impl Addr {
    fn try_from(ip: &[&str; 4], port: u16) -> Result<Addr, ParseIntError> {
        seq!(N in 0..4 {
            Ok(Addr {
                ip: [ #( ip[N].parse()?, )* ],
                port,
            })
        })
    }
}

fn main() {
    println!("{:?}", Addr::try_from(&["1", "23", "45", "67"], 80));
    println!("{:?}", Addr::try_from(&["1", "23", "45", "foo"], 80));
}

Output:

Ok(Addr { ip: [1, 23, 45, 67], port: 80 })
Err(ParseIntError { kind: InvalidDigit })

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 aedm