5

The following code fails with a borrow error:

extern crate chrono; // 0.4.6

fn main() {
    let mut now = chrono::Local::today();
    now = std::mem::replace(&mut now, now.succ());
}

The error is:

error[E0502]: cannot borrow `now` as immutable because it is also borrowed as mutable
 --> src/lib.rs:5:39
  |
5 |     now = std::mem::replace(&mut now, now.succ());
  |           ----------------- --------  ^^^ immutable borrow occurs here
  |           |                 |
  |           |                 mutable borrow occurs here
  |           mutable borrow later used by call

Why is there a borrow error here? now.succ() returns a new object, and it would look like the succ() call should return the new object, end the immutable borrow before the mutable borrow occurs with replace.

Listerone
  • 1,381
  • 1
  • 11
  • 25

1 Answers1

4

The order of the arguments matters. For example this works:

/// Same as `std::mem::replace`, but with the reversed parameter order.
pub fn replace<T>(src: T, dest: &mut T) -> T {
    std::mem::replace(dest, src)
}

fn main() {
    let mut now = chrono::Local::today();
    now = replace(now.succ(), &mut now);
}

(link to playground)

But in your example, &mut now appears first, and when evaluating the second parameter, it is already borrowed.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
mcarton
  • 27,633
  • 5
  • 85
  • 95
  • Why `now = replace(now.succ(), &mut now);` ? look strange xd it's a do nothing line – Stargateur Apr 30 '19 at 14:42
  • That would be a compiler limitation then. – Listerone Apr 30 '19 at 14:46
  • 1
    @Stargateur it never even crossed my mind to make that example function work. I was even going to type `unimplemented`()` but got too lazy to do even this. – mcarton Apr 30 '19 at 14:55
  • @Listerone while the order wouldn't actually matter for borrowing in this example, for more complex examples with different lifetimes and references, it could matter, so the compiler must at some point set a line. – mcarton Apr 30 '19 at 14:56
  • @mcarton Is that really true? Since the immutable borrow is a call that must return before replace can actually receive the mutable reference to now, is there _any_ scenario where these two could interact other than as expected? – jspencer Aug 08 '19 at 06:33
  • I thought function call argument evaluation order was unspecified. – hkBst Mar 18 '22 at 16:28
  • @hkBst in Rust it is specified, but even if it weren’t the borrow checker could be more constrained that the generated code. – mcarton Mar 18 '22 at 18:16