Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can an IpAddr be converted to an IPv4Addr?

Tags:

rust

In Rust, it is easy to create an IpAddr from an IpV4Addr:

let ipv4 = IpV4Addr::new(127,0,0,1);
let ip = IpAddr::V4(ipv4);

But it seems that there is no way back:

if ip.is_ipv4() {
    let ipv4: IpV4Addr = .....?
}

The reason is that I need to access the octets of the IPv4 address at some point (via IPv4::octets) to transfer them over the network. However, I'd like to use the more generic IpAddr representation in other parts of the application, since I want to implement IPv6 in the future, therefore making the network code not IPv4-specific.

like image 243
Philipp Ludwig Avatar asked Feb 23 '18 23:02

Philipp Ludwig


2 Answers

IpAddr is an abstraction for IP addresses. It is an enum which holds either an Ipv4Addr or Ipv6Addr and some methods to provide abstract access to either.

Rust won't let you cast an IpAddr to an Ipv4Addr because what if it's actually an Ipv6Addr? You have to handle that case as well. You convince Rust that you've handled all possibilities with match.

use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

fn main() {
    let ips = [
        IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
        IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
    ];

    for ip in ips.iter() {
        match *ip {
            IpAddr::V4(ip4) => println!("ipv4: {}, octets: {:?}", ip4, ip4.octets()),
            IpAddr::V6(ip6) => println!("ipv6: {}, segments: {:?}", ip6, ip6.segments()),
        }
    }
}

There is no need for a default case because the Rust type checker knows there is no other possibility. ips is an array of IpAddr and that contains only two possibilities, V4 or V6. In fact, Rust will warn if you put one there so should IpAddr change in the future you will be made aware with a compile error.

like image 61
Schwern Avatar answered Oct 05 '22 00:10

Schwern


IpAddr is an enum, so you can use pattern matching:

if let IpAddr::V4(ipv4) = ip {
    // here ipv4 is of type IpV4Addr
}

// or, if you need to handle IPv6 as well

match ip {
    IpAddr::V4(ipv4) => { /* handle IPv4 */ }
    IpAddr::V6(ipv6) => { /* handle IPv6 */ }
}

Also, IpAddr is Copy, so you don't even need to worry about consuming its original value (the ip variable), it will just be implicitly copied for you.

like image 44
Vladimir Matveev Avatar answered Oct 05 '22 00:10

Vladimir Matveev