1
struct Foo {
    a: Vec<i32>,
}

impl Foo {
    fn bar(&mut self) -> &i32 {
        if let Some(x) = self.a.iter().find(|i| **i == 0) {
            return x;
        }
        self.a.push(0);
        self.a.last().unwrap()
    }
}

(Playground)

This code looks semantically correct to me. It should either return a reference to an existing element in the vector or create a new element and return a reference to it. But I get the following error:

error[E0502]: cannot borrow `self.a` as mutable because it is also borrowed as immutable
  --> src/lib.rs:10:9
   |
6  |     fn bar(&mut self) -> &i32 {
   |            - let's call the lifetime of this reference `'1`
7  |         if let Some(x) = self.a.iter().find(|i| **i == 0) {
   |                          ------ immutable borrow occurs here
8  |             return x;
   |                    - returning this value requires that `self.a` is borrowed for `'1`
9  |         }
10 |         self.a.push(0);
   |         ^^^^^^^^^^^^^^ mutable borrow occurs here

Is there a way to make this code compile?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
robson
  • 413
  • 4
  • 8
  • 1
    [The duplicate applied to your situation](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7b750dcdd855aaef06ccb7133d93c9ee) – Shepmaster Feb 12 '19 at 18:40
  • Thanks. I knew I could work around with an index. But my real use case is that I have a semaphore and a vector of mutexes. The idea is that I would try each mutex in the vector with try_lock and return the MutexGuard and a SemaphoreGuard together. When all mutexes are locked the semaphore will block and it will avoid a busy loop trying try_lock() in all mutexes until one was free. In that case I can't use indexes. – robson Feb 12 '19 at 18:51
  • I generally can't solve problems with code I can't see, so I don't have much help to give here. It's *possible* that this could be a place to make judicious use of `unsafe` code to sidestep the borrow checker. – Shepmaster Feb 12 '19 at 18:56
  • Something like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3d04fbf943ffcbbc57f618f0a82e92ad Except that the playground won't let me use a crate. – robson Feb 12 '19 at 19:08
  • Not [that specific crate, no](https://play.rust-lang.org/help#features-crates). – Shepmaster Feb 12 '19 at 19:10
  • Is there an unsafe version of vector::push? – robson Feb 12 '19 at 20:19
  • I've found out that RUSTFLAGS="-Z borrowck=mir -Z polonius" cargo run accepts the original code I posted. – robson Feb 12 '19 at 20:27
  • There's no concept of an unsafe push, no. You have to re-create the reference so that the compiler doesn't know they are associated. [An example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=15c10911ef09190e7d1113ebce7bebd2). That being said, your concept has larger problems that the compiler cannot see through because mutating the `Vec` invalidates the references to everything inside of it. – Shepmaster Feb 13 '19 at 00:15
  • I think the concept doesn't really have problems. The mutation only happens if I'm not holding the reference anymore. The compiler even agrees with me when I use the -Z polonius flag. Thanks for posting the example on how to work around the borrow checker in this case. – robson Feb 13 '19 at 00:22

0 Answers0