A newtype can make invalid raw values hard to pass around by validating them once at construction.

Program

Play the program to choose a raw port and fall back when validation rejects it.

newtype_port_validation.rs
#[derive(Clone, Copy)]
struct Port(u16);

impl Port {
    fn new(value: u32) -> Option<Self> {
        if (1..=65535).contains(&value) {
            Some(Self(value as u16))
        } else {
            None
        }
    }
}

fn main() {
    let raw = ;
    let port = Port::new(raw).unwrap_or(Port(80));
    println!("port={}", port.0);
}
#[derive(Clone, Copy)]
struct Port(u16);

impl Port {
    fn new(value: u32) -> Option<Self> {
        if (1..=65535).contains(&value) {
            Some(Self(value as u16))
        } else {
            None
        }
    }
}

fn main() {
    let raw = ;
    let port = Port::new(raw).unwrap_or(Port(80));
    println!("port={}", port.0);
}
#[derive(Clone, Copy)]
struct Port(u16);

impl Port {
    fn new(value: u32) -> Option<Self> {
        if (1..=65535).contains(&value) {
            Some(Self(value as u16))
        } else {
            None
        }
    }
}

fn main() {
    let raw = ;
    let port = Port::new(raw).unwrap_or(Port(80));
    println!("port={}", port.0);
}
newtype `Port` wraps `u16` so APIs can ask for a validated port instead of a raw number.
constructor `Port::new` returns `Option<Port>` to make invalid input explicit.
fallback `unwrap_or` chooses a default only after validation has failed.