How to cast enums to an underlying type?

I guess this is a beginner question.

I posted a question on SO, and of course it was marked as answered somewhere else, but I'm only a little bit closer to figuring this out. I would appreciate any help, even if it's just pointing me to the right place in the Rust Programming Language book or another forum.

Here is the link to the question, but I will summarize here as well:

The code looks like this:

pub enum SocketAddr {
    V4(SocketAddrV4),
    V6(SocketAddrV6),
}

pub fn new<A: ToSocketAddrs>(local_addr: A, _: Option<u32>) -> io::Result<UdpConnector> {
    let mut addr = net::addr_from_trait(local_addr)?;
    debug!("Attempting to connect to {:?}", addr);
    UdpSocket::bind(addr).expect(&format!("Couldn't bind to address {:?}", addr))
}

I can call is_ipv6() on addr, and if it is ipv6, then I want to call set_scope_id() on it. However, in order to do that, I need to access it as a SocketAddrV6.

The following seems to work (I think), but I don't understand the syntax. I don't know where "c" comes from. Seems like it is a placeholder? Am I doing the right thing here?

    let mut addr2 = if let SocketAddr::V6(c) = addr {c} else { unreachable!() };
    addr2.set_scope_id(2)

(This code assumes that addr has already been determined to be of type SocketAddrV6.)

Thanks and best regards.

The key concept here is pattern matching. The if let syntax matches a value (addr) against a pattern (SocketAddr::V6(c)) and enters the if block only if the value matches the pattern. The c introduces a binding for the SocketAddrV6 value held within the SocketAddr::V6 variant. This means that if addr matches the pattern, the inner SocketAddrV6 value is assigned to the variable c. The if-let line in your example is equivalent to

let mut addr2 = match addr {
    SocketAddr::V6(c) => c,
    _ => unreachable!()
};

This is all covered in Chapter 6 of the book, and if let syntax in particular is in Chapter 6 section 3.
Hope that helps!

2 Likes

Thanks, Nathen! It helps just knowing what I did was more or less correct.

I do now see what you are talking about. Part of it is explained with some code in section 6.2 that looks like this:

https://doc.rust-lang.org/book/ch06-02-match.html

#![allow(unused_variables)]
fn main() {
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}

And then the 'if let' syntax is in 6.3, as you said. I'll have to think about this until it seems more natural, but this basically gets me back on track. Much appreciated!