2

I have a segment of Rust code which, simplified, looks something like this:

struct FooRef<'f>(&'f mut i32);

fn blah<'f>(f: &'f mut i32) -> FooRef<'f> {
    let mut i = 0;
    loop {
        let fr = FooRef(f);
        if i == *fr.0 {
            return fr;
        }
        i += 1;
    }
}

fn main() {
    let mut f = 5;
    blah(&mut f);
}

playground link

It doesn't compile:

error[E0499]: cannot borrow `*f` as mutable more than once at a time
  --> test.rs:6:25
   |
6  |         let fr = FooRef(f);
   |                         ^
   |                         |
   |                         second mutable borrow occurs here
   |                         first mutable borrow occurs here
...
12 | }
   | - first borrow ends here

If I understand this error, it's saying that f can be borrowed until the end of the function on one loop iteration, then borrowed again on a second iteration, which doesn't work.

This doesn't seem right to me; either fr goes out of scope (and thus is returned before it says) or it returns (and there's no subsequent borrow).

Is there any way to express this that will compile?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Scott Lamb
  • 2,266
  • 1
  • 19
  • 21
  • 1
    You will find a lot of duplicate of this problem, here the fix, https://play.rust-lang.org/?gist=31b82f9856dfe3695468e1d350632b32&version=stable. – Stargateur Jul 21 '17 at 00:18
  • Hmm, that's not what I was hoping for. In my real logic, the equivalent of `FooRef(f)` does something non-trivial; I want to ensure that `FooRef::drop` runs when the `FooRef` goes out of scope. Is that really the only option? – Scott Lamb Jul 21 '17 at 02:48
  • 1
    You could declare the variable before the loop, I'm not fully qualified to know if there are other options. Maybe use RefCell. Maybe your [mcve] is too minimal to represent your situation ? – Stargateur Jul 21 '17 at 03:49
  • 1
    I couldn't find a dupe on SO but here's a discussion of the same issue: https://github.com/rust-lang/rust/issues/21906. It's a borrow checker limitation that should be fixed by [non-lexical lifetimes](https://github.com/rust-lang/rust-roadmap/issues/16) if that ever gets implemented. – interjay Jul 21 '17 at 08:29
  • @Stargateur to answer your question about my example, the difference between this and the real one is essentially ref-counting. The details don't seem that relevant to me, but if you really want them: I'm trying to make my own ffmpeg wrapper. This function is being passed a (wrapper around an) `AVPacket`, which gets filled by a call to `av_read_frame`. Once it's filled, the packet has a reference which must be released via `av_packet_unref`; `FooRef` does that. The loop is discarding packets that aren't of interest; there may be both audio and video packets, and I only want the video ones. – Scott Lamb Jul 21 '17 at 14:12
  • 1
    Here's the duplicate's answer [applied to your code](https://play.integer32.com/?gist=8017bb807514fdd410ebdbf9a8798b00&version=stable). TL;DR: you need to hold the compilers hand a little bit and be explicit about the moves of the mutable reference. – Shepmaster Jul 21 '17 at 14:43

0 Answers0