fadedbee
fadedbee

Reputation: 44807

How to use mutable state in a loop?

The loop:

impl State {    
    pub fn exec(&mut self) -> Vec<(&[u8], SocketAddr)> {
        vec!()
    }
}

fn main_loop(mut state: State) {
    let mut packets = Vec::<(&[u8], SocketAddr)>::new();
    loop {
        let new = state.exec();
        packets.extend(new);
    }
}

gives the error:

error[E0499]: cannot borrow `state` as mutable more than once at a time
  --> src/main.rs:93:14
   |
93 |             let new = state.exec();
   |                       ^^^^^ `state` was mutably borrowed here in the previous iteration of the loop

Yet if I comment-out the packets.extend(new); line, there is no error.

I think the problem is that the returned vector is owned by state, as if new is dropped before the loop iterates, it's fine. I'm happy for the returned vector to be copied, if this would fix it.

I expected that adding .clone() to either new or state.exec() would have fixed it, but it doesn't.

Is there a simple way of fixing this?

Or do I need to structure single-threaded application state in a different way?

Upvotes: 1

Views: 2290

Answers (3)

fadedbee
fadedbee

Reputation: 44807

One solution is:

impl State {    
    pub fn exec(&mut self) -> Vec<(Vec<u8>, SocketAddr)> {
        vec!()
    }
}

fn main_loop(mut state: State) {
    let mut packets = Vec::<(Vec<u8>, SocketAddr)>::new();
    loop {
        let new = state.exec();
        packets.extend(new);
    }
}

I'm not sure that this is the best solution, so I'll leave the question open for a while.

Upvotes: 1

trent
trent

Reputation: 28025

Just changing the function signature so that the returned &[u8] does not borrow from *self will allow the example code to compile:

    pub fn exec<'a>(&mut self) -> Vec<(&'a [u8], SocketAddr)> {
        vec![]
    }

Note that 'a is a free lifetime parameter, so the caller can supply anything there (including 'static) and whatever references exec returns will have to satisfy the caller-supplied lifetime. In practice, this means that the Vec can only contain 'static references, because 'static is the only lifetime that is a subtype of every other lifetime.

It's unlikely this is what you want. However, it might be, or the [u8] might be borrowed from some other argument (not *self), or re-borrowed from a reference contained in *self. This shows that simply changing the signature to more accurately represent the lifetime relationships between inputs and outputs can sometimes fix errors like this one without any other changes.

Upvotes: 2

Silvio Mayolo
Silvio Mayolo

Reputation: 70347

pub fn exec(&mut self) -> Vec<(&[u8], SocketAddr)>

Let's add in the implicit lifetimes

pub fn exec<'a>(&'a mut self) -> Vec<(&'a [u8], SocketAddr)>

So the returned slice must have the same lifetime as the mutable borrow. Now, what do you do with the returned slice?

let new = state.exec();
packets.extend(new);

We store it in packets. So the borrow of state must last at least at long as the type of packets, which is the whole function. Hence, from the first iteration of the loop to the very end of the function, we can never borrow state again, since the mutable borrow lasts until the end of the function.

The first question to ask yourself is: do you really need a mutable borrow? If you can get down to &self, then do that and your problem is solved. Otherwise, you'll need to restructure things somehow. The type &[u8] is inherently unowned. It's a reference to someone else's data. If the only way you have to get that data is through &mut self, then you'll never be able to loop the way you want. So to clone the data, you'll need to use to_vec, which turns &[u8] into Vec<u8> (your type signatures will have to change to reflect this).

Upvotes: 13

Related Questions