Putting thing (moving) inside struct gives lifetime error

Viewed 178

First of all, I know it's much better to create minimal reproducible examples, but I don't have idea on where this problem comes from. I've cleared much of the code for this question.

#[derive(Debug)]
pub struct Item<'a, 'b: 'a> {
    socket: Socket<'a, 'b>,
    refs:   usize
}
#[derive(Debug)]
pub struct SocketSet<'a, 'b: 'a, 'c: 'a + 'b> {
    sockets: ManagedSlice<'a, Option<Item<'b, 'c>>>
}

impl<'a, 'b: 'a, 'c: 'a + 'b> SocketSet<'a, 'b, 'c> {
    pub fn new<SocketsT>(sockets: SocketsT) -> SocketSet<'a, 'b, 'c>
            where SocketsT: Into<ManagedSlice<'a, Option<Item<'b, 'c>>>> {
        let sockets = sockets.into();
        Set {
            sockets: sockets
        }
    }

pub struct TunSmolStack<'a, 'b, 'c> {
    sockets: SocketSet<'a, 'b, 'c>,
}

impl<'a, 'b, 'c> TunSmolStack<'a, 'b, 'c> {
    pub fn new(interface_name: String) -> Result<TunSmolStack<'a, 'b, 'c>, u32> {
        let socket_set = SocketSet::new(vec![]);
        Ok(TunSmolStack{
            sockets: socket_set,
        })
    }

Here's the full error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements
  --> src/virtual_tun/smol_stack.rs:26:12
   |
26 |         Ok(TunSmolStack{
   |            ^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'b` as defined on the impl at 16:10...
  --> src/virtual_tun/smol_stack.rs:16:10
   |
16 | impl<'a, 'b, 'c> TunSmolStack<'a, 'b, 'c> {
   |          ^^
note: ...so that the expression is assignable
  --> src/virtual_tun/smol_stack.rs:26:12
   |
26 |           Ok(TunSmolStack{
   |  ____________^
28 | |             sockets: socket_set,
29 | |         })
   | |_________^
   = note: expected  `virtual_tun::smol_stack::TunSmolStack<'_, 'b, 'c>`
              found  `virtual_tun::smol_stack::TunSmolStack<'_, '_, '_>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `std::option::Option<virtual_tun::interface::smoltcp::socket::SocketSetItem<'_, '_>>` will meet its required lifetime bounds
  --> src/virtual_tun/smol_stack.rs:22:26
   |
22 |         let socket_set = SocketSet::new(vec![]);
   |                          ^^^^^^^^^^^^^^

It is complaining about SocketSet. Ok, possibly something related to the inner objects of the socket_set not living enough? Why it talks about static lifetime?

PS: putting my mouse on top of socket_set on

let socket_set = SocketSet::new(vec![]);

gives this type for the variable:

virtual_tun::interface::smoltcp::socket::SocketSet<'a, 'static, 'static>

Well, the lifetime 'a for the SocketSet, as defined here:

    pub struct SocketSet<'a, 'b: 'a, 'c: 'a + 'b> {
        sockets: ManagedSlice<'a, Option<Item<'b, 'c>>>
    }

is the lifetime of the thing stored inside ManagedSlice, which is Option<Item<'b, 'c>>

So, the anonymous vec that I pass to the constructor of SocketSet is converted into a reference to slice with lifetime 'a, and the things inside the slice have static lifetime. Don't know why, but they have. I guess the problem is that it requires that 'b from SmolStack should be 'static?

ps: I don't have control over SocketSet, SocketSet::new, etc...

1 Answers

Its because of the lifetime constraint on sockets in SocketSet::new. ManagedSlice Requires that its value to live as long as 'a seen in its definition ManagedSlice<'a, T: 'a>.

The generic constraint SocketsT: Into<ManagedSlice<'a, Option<Item<'b, 'c>>> does not assert that the ManagedSlice is Owned. So the compiler will make sure that this code works regardless if the into returns a Owned or Borrowed. ManagedSlice has to live for the longest lifetime of ManagedSlice::Owned and ManagedSlice::Borrowed because it could be either.

Calling .into() on sockets does creates a ManagedSlice::Owned in this case, but in a situation where into returned ManagedSlice::Borrowed, into would create a reference. So it makes 'a at least as long as 'static, as sockets is owned by the function and a is a reference to sockets.

Changing the generic constraints to make it clear that what is being passed in is not a reference gets rid of the warning.

impl<'a, 'b: 'a, 'c: 'a + 'b> SocketSet<'a, 'b, 'c> {
    pub fn new<SocketsT>(sockets: SocketsT) -> SocketSet<'a, 'b, 'c>
            where SocketsT: Into<Vec<Option<Item<'b, 'c>>>> {
        SocketSet {
            sockets: ManagedSlice::Owned(sockets.into())
        }
    }
}

The error will re-emerge if you modify the code to

sockets: ManagedSlice::from(sockets.into())

Because again, it is not clear if from is going to return Owned or Borrowed.

You can also simplify a lot of the lifetimes away from your code to make it more readable.

#[derive(Debug)]
pub struct Item<'a> {
    socket: Socket<'a, 'a>,
    refs:   usize
}
#[derive(Debug)]
pub struct SocketSet<'a> {
    sockets: ManagedSlice<'a, Option<Item<'a>>>
}

impl<'a> SocketSet<'a> {
    pub fn new<SocketsT>(sockets: SocketsT) -> SocketSet<'a>
            where SocketsT: Into<Vec<Option<Item<'a>>>> {
        SocketSet {
            sockets: ManagedSlice::Owned(sockets.into())
        }
    }
}
Related