2

When a mutable argument is passed as a function argument, the borrow checker doesn't allow this to be used to construct other arguments, even when those arguments clone values without holding a reference.

While assigning variables outside the function is always an option1, logically this seems over zealous and something the borrow checker could take into account.

Is this working as intended or something that should be resolved?

#[derive(Debug)]
struct SomeTest {
    pub some_value: f64,
    pub some_other: i64,
}

fn some_fn(var: &mut SomeTest, other: i64) {
    println!("{:?}, {}", var, other);
}

fn main() {
    let mut v = SomeTest { some_value: 1.0, some_other: 2 };
    some_fn(&mut v, v.some_other + 1);

    // However this works!
/*
    {
        let x = v.some_other + 1;
        some_fn(&mut v, x);
    }
*/
}

Gives this error:

  --> src/main.rs:14:21
   |
14 |     some_fn(&mut v, v.some_other + 1);
   |                  -  ^^^^^^^^^^^^ use of borrowed `v`
   |                  |
   |                  borrow of `v` occurs here

See: playpen.


[1]: Even though one-off assignments do sometimes improve readability, being forced to use them for arguments encourages use of scopes to avoid single use variables polluting the name-space, causing function calls that would otherwise be one line - being enclosed in braces and defining variables... I'd like to avoid this if possible especially when the requirement seems like something the borrow checker could support.

ideasman42
  • 42,413
  • 44
  • 197
  • 320

1 Answers1

5

This is an artifact of the current implementation of the borrow checker. It is a well known limitation, dating back to at least 2013, and no one is overjoyed by it.

Is this working as intended

Yes.

something that should be resolved?

Yes.

The magic keywords are "non-lexical lifetimes". Right now, lifetimes are lexical - they correspond to the blocks of source that we type. Ideally, foo.method(foo.mutable_method()) would see that the borrow ends "inside the parenthesis", but for a myriad of reasons, it is tied to the entire statement.

For tons of further information see RFC issue 811 and everything linked from there.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    I'm *certain* that this has been answered before, but I cannot find a duplicate. If you find it, let me know and I'll close it. – Shepmaster Jan 02 '17 at 04:04
  • Wow, I've just stumbled on a very similar issue. It surprised me that `vec.drain(vec.len() - 1..)` is not allowed, while `let vlen = vec.len(); vec.drain(vlen - 1..)` compiles just fine. The workaround is trivial, but it feels wrong, as one could easily imagine the former practically *desugaring* into something like the latter. I assume the underlying cause is the same? – user4815162342 Jan 02 '17 at 21:56
  • 1
    @user4815162342 yes. – Shepmaster Jan 02 '17 at 22:08
  • 1
    Surprising this isn't high priority to resolve? AFAICS this is a fairly simple example where *borrow-checker-induced-damage* will be added to otherwise straightforward and safe code. I can understand that some complex scenarios will need workarounds, but this seems like a case that is likely to happen frequently. It also makes using methods more of a hassle then regular functions since accessing the method borrows too. Edit, maybe it is high priority - Issue 811 has >50 likes :) – ideasman42 Jan 03 '17 at 03:17