CrepeGoat
CrepeGoat

Reputation: 2515

mutable borrows in a loop

My code:

struct Foo<'a> {
    bytes: &'a mut [u8],
}

impl<'a> Foo<'a> {
    fn bar<'b>(&'b mut self) {
        self.bytes = &mut self.bytes[1..];
    }
}

fn baz<'a, 'b>(foo: &'b mut Foo<'a>) {
    println!("baz!");
    
    for _ in 0..3 {
        foo.bar();
    }
}

Also here in a Rust Playground.

This results in the following error traceback:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/main.rs:12:27
   |
12 |         self.bytes = &mut self.bytes[1..];
   |                           ^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'b` as defined here...
  --> src/main.rs:11:12
   |
11 |     fn bar<'b>(&'b mut self) {
   |            ^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:12:27
   |
12 |         self.bytes = &mut self.bytes[1..];
   |                           ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
  --> src/main.rs:10:6
   |
10 | impl<'a> Foo<'a> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:12:22
   |
12 |         self.bytes = &mut self.bytes[1..];
   |                      ^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground` due to previous error

Goal

I'm looking to change the function signatures of bar / baz to get this to compile. My understanding was that I needed to specify lifetime bounds on 'a & 'b and it would work, but I get different errors for both 'b: 'a and 'a: 'b in either/both functions.

I've also looked through ~10 other "mutable borrow in loop" questions, but I don't understand the answers well enough to apply them to my problem, so even if this is a duplicate I'd appreciate an answer relevant to this toy example specifically.

Any help is appreciated!

Upvotes: 0

Views: 279

Answers (2)

vikram2784
vikram2784

Reputation: 822

The problem here is the borrow happens through 'b. ('b and 'a are disjoint). The compiler wants you to guarantee the reference 'b needs to be atleast as long as 'a. So we should look for a way where 'b isn't involved when (re)borrowing the slice, as 'b is considered to be valid only for the function call duration. There is no return or an "outgoing" lifetime from the call.

You can use the std::mem::* functions to achieve this

Upvotes: 3

Caesar
Caesar

Reputation: 8484

@vikram2784's solution works, but can be expressed a bit more concisely by only replacing bytes, not the entire Foo:

impl<'a> Foo<'a> {
    fn bar(&mut self) {
        let tmp = &mut [][..];
        let bytes = std::mem::replace(&mut self.bytes, tmp);
        self.bytes = &mut bytes[1..];
    }
}

This pattern is also easier to use if Foo contains some additional fields that don't have obvious default values.

One thing to note about this kind of solution: If the bytes[1..] panics, the empty temporary will be left in place. Here, the only reason for a panic I can think of is bytes being empty, so it won't matter, but depending on the situation, you may get bitten by it.

Upvotes: 6

Related Questions