Help with a complicated Deref case


#1

Hi, I am having trouble using Deref in this more complicated case. Hoping someone can help, or let me know if it’s impossible. The full context is at: https://github.com/krisprice/ipnet/blob/master/src/ipnet.rs

I have an enum IpNet that contains two structs Ipv4Net and Ipv6Net as follows.

pub enum IpNet {
    V4(Ipv4Net),
    V6(Ipv6Net),
}

pub struct Ipv4Net {
    addr: Ipv4Addr,
    prefix_len: u8,
}

pub struct Ipv6Net {
    addr: Ipv6Addr,
    prefix_len: u8,
}

For the Ipv4Net and Ipv6Net types I am using Deref to call methods on the contained Ipv4Addr and Ipv6Addr types respectively, as follows. This works fine, as expected.

impl Deref for Ipv4Net {
    type Target = Ipv4Addr;
    fn deref(&self) -> &Self::Target {
        &self.addr
    }
}

impl Deref for Ipv6Net {
    type Target = Ipv6Addr;
    fn deref(&self) -> &Self::Target {
        &self.addr
    }
}

But I have no idea how to implement this for the IpNet enum, or if it’s even possible, my very naive attempt at trying to make this work below seems clumsy and doesn’t work.

impl Deref for IpNet {
    type Target = IpAddr;
    fn deref(&self) -> &Self::Target {
        match *self {
            IpNet::V4(ref n) => &IpAddr::from(n.addr.clone()),
            IpNet::V6(ref n) => &IpAddr::from(n.addr.clone()),
        }
    }
}

This results in the error below. (Just showing one of the errors, but it’s repeated for each match arm.)

   --> src/ipnet.rs:197:34
    |
197 |             IpNet::V4(ref n) => &IpAddr::from(n.addr.clone()),
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^-
    |                                  |                          |
    |                                  |                          temporary value only lives until here
    |                                  does not live long enough
    |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 195:5...
   --> src/ipnet.rs:195:5
    |
195 | /     fn deref(&self) -> &Self::Target {
196 | |         match *self {
197 | |             IpNet::V4(ref n) => &IpAddr::from(n.addr.clone()),
198 | |             IpNet::V6(ref n) => &IpAddr::from(n.addr.clone()),
199 | |         }
200 | |     }
    | |_____^

#2

It’s not possible with how your types are defined. Deref ties the lifetime of the returned reference with the lifetime of &self, which is a reference to your enum. Since your enums don’t own an IpAddr you have nothing to borrow it from (to return it as a reference).

Perhaps you just want to implement From to allow the conversion? That’s semantically closer to what’s happening here.


#3

.clone() allocates a new object, but & means the object does not need to be freed. These things are at odds with each other, so it won’t work.

In general, in such functions returning a reference you won’t be able to create any new information. You can only return reference to already-existing field in exactly the form it exists in.


#4

Thanks @vitalyd and @kornel – sounds like in this situation it won’t be possible. Can’t Deref to something that doesn’t exist in this case. To make this work it sounds like I would need to create and store the IpAddr version of the contained Ipv4Addr or Ipv6Addr someplace and return the reference to that. I think I’ll forgo having Deref’s in this case and just duplicate the methods from IpAddr.

Thanks!


#5

@krisprice, I think you can simply return an IpAddr from your enum - IpAddr has a From<Ipv4Addr> and From<Ipv6Addr> already. Alternatively, you can implement From<IpNet> for IpAddr and/or From<&'a IpNet> for IpAddr if you want the more generic From/Into pair for other reasons.