Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Immutable borrow as argument for mutable borrow does not compile #34035

Open
skiwi2 opened this issue Jun 2, 2016 · 12 comments
Open

Immutable borrow as argument for mutable borrow does not compile #34035

skiwi2 opened this issue Jun 2, 2016 · 12 comments
Labels
A-borrow-checker Area: The borrow checker C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@skiwi2
Copy link

skiwi2 commented Jun 2, 2016

I have the following piece of code which I expect to work but the borrow checker does not agree:

fn main() {
    let mut data = vec![1; 100];
    {
        let (left, right) = data.split_at_mut(data.len() / 2);
    }
    println!("{:?}", data);
}

Please note that the extra scope is in principle not related to the issue I'm describing, I'm just doing it such that I can use the println! macro to ensure that the compiler does not optimize the code away because I'm not using it thereafter. (I'm new to Rust so I don't know the exact rules)

The error I get is the following:

main.rs:4:47: 4:51 error: cannot borrow `data` as immutable because it is also borrowed as mutable [E0502]
main.rs:4         let (left, right) = data.split_at_mut(data.len() / 2);
                                                        ^~~~
main.rs:4:29: 4:33 note: previous borrow of `data` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `data` until the borrow ends
main.rs:4         let (left, right) = data.split_at_mut(data.len() / 2);
                                      ^~~~
main.rs:5:6: 5:6 note: previous borrow ends here
main.rs:3     {
main.rs:4         let (left, right) = data.split_at_mut(data.len() / 2);
main.rs:5     }
              ^

However what I would expect is the following:

  1. data.len() takes an immutable borrow.
  2. data.len() returns the immutable borrow and divides the obtained u32 by 2.
  3. data.split_at_mut() takes a mutable borrow.

It seems like the borrow checker currently evaluates the statement in the wrong order.

The following workaround is available, but it feels like this is a place where we should not have the need to (rightfully) fight the borrow checker.

fn main() {
    let mut data = vec![1; 100];
    {
        let len = data.len();
        let (left, right) = data.split_at_mut(len / 2);
    }
    println!("{:?}", data);
}
@skiwi2 skiwi2 changed the title Immutable borrow as argument for mutable borrow Immutable borrow as argument for mutable borrow does not compile Jun 2, 2016
@punmechanic
Copy link

I'm going to naively assume this is because data.split_at_mut(data.len()) is a singular expression and you could theoretically access the immutable reference of data (from data.len()) at the same time as the returned mutable reference, which would violate the borrowing rules. If that's the case it sort-of makes sense why this is the case.

@apasel422
Copy link
Contributor

I think this is being tracked at rust-lang/rfcs#811.

@durka
Copy link
Contributor

durka commented Jun 2, 2016

Yeah, non-lexical borrows should fix this case. Until then you can use unborrow.

@Stebalien
Copy link
Contributor

I'm not entirely sure that non-lexical borrows can fix this without being a breaking change. The above code desugars to:

fn main() {
    let mut data = vec![1; 100];
    {
        let (left, right) = DerefMut::deref_mut(&mut data).split_at_mut(Deref::deref(&data).len() / 2);
    }
    println!("{:?}", data);
}

@durka
Copy link
Contributor

durka commented Jun 2, 2016

@Stebalien I think non-lexical borrowck should allow that code. len does not return a borrow, so the immutable borrow is over after the parameters are evaluated and before the split_at_mut call.

@Stebalien
Copy link
Contributor

If we deref in order, we could write code that could print out the following:

* Mutably dereferenced! Mutations allowed until next immutable deref.
* Immutably dereferenced! Mutations forbidden until next mutable deref.
* Mutated!

Alternatively, we could unborrow and reborrow:

* Mutably dereferenced! Mutations allowed until next immutable deref.
* Immutably dereferenced! Mutations forbidden until next mutable deref.
* Mutably dereferenced! Mutations allowed until next immutable deref.
* Mutated!

Or borrow late:

* Immutably dereferenced! Mutations forbidden until next mutable deref.
* Mutably dereferenced! Mutations allowed until next immutable deref.
* Mutated!

However, that's also a breaking change.

Of course, one could argue that the behavior here is unspecified...

@durka
Copy link
Contributor

durka commented Jun 2, 2016

Oh, indeed I missed the first deref_mut. Perhaps unborrow will remain
relevant!
On Jun 2, 2016 2:14 PM, "Steven Allen" notifications@github.com wrote:

If we deref in order, we could write code that could print out the
following:

  • Mutably dereferenced! Mutations allowed until next immutable deref.
  • Immutably dereferenced! Mutations forbidden until next mutable deref.
  • Mutated!

Alternatively, we could unborrow and reborrow:

  • Mutably dereferenced! Mutations allowed until next immutable deref.
  • Immutably dereferenced! Mutations forbidden until next mutable deref.
  • Mutably dereferenced! Mutations allowed until next immutable deref.
  • Mutated!

Or borrow late:

  • Immutably dereferenced! Mutations forbidden until next mutable deref.
  • Mutably dereferenced! Mutations allowed until next immutable deref.
  • Mutated!

However, that's also a breaking change.

Of course, one could argue that the behavior here is unspecified...


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#34035 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AAC3n3yJcTSvGkIcgqZKQneM_5CMjcEYks5qHx18gaJpZM4Iseiw
.

@nikomatsakis
Copy link
Contributor

@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 25, 2017
@estebank
Copy link
Contributor

Current output:

error[E0502]: cannot borrow `data` as immutable because it is also borrowed as mutable
 --> src/main.rs:4:47
  |
4 |         let (left, right) = data.split_at_mut(data.len() / 2);
  |                             ---- ------------ ^^^^ immutable borrow occurs here
  |                             |    |
  |                             |    mutable borrow later used by call
  |                             mutable borrow occurs here

@crlf0710 crlf0710 added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jun 11, 2020
@Xenomojin
Copy link

What is the current state of this issue?
It's now Feb. 2021 and I'm still getting this error. Are there any plans or is and will be the only solution to outsource the needed parameters into a separate variable beforehand?

I am relatively new to Rust and I don't think I fully understand the borrow checker yet, but this 'workaround' shouldn't be the way to go shouldn't it?

@estebank
Copy link
Contributor

estebank commented Feb 3, 2021

@Xenomojin for a case like the one above, you must bind the value of data.len() (which is Copy) to a new binding (let len = data.len();) and use that in the call. This is necessary because the borrow checker cannot know for sure that the immutable borrow that .len() needs doesn't overlap with the mutable borrow split_at_mut needs. This is a case we want to improve in the future, but it will require subtle handling that isn't yet done.

@Xenomojin
Copy link

Thank you very much for your response @estebank, I guess I just need to get used to it. I mean... its not that much of a cost compared to knowing for sure there won't be anything unforeseen at run time, unlike some C code where I was spending hours and hours not getting forward at all. But I'm seeing forward to having handled this little bumpiness in an improved manner some day in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants