Permit mutation of `&mut` pointers whose pointees have been borrowed
Nobody has claimed this yet.
- Dominant language
- Markdown
- Stars
- 6.6k
- Forks
- 1.7k
- Avg merge
- 16h 14m
- Merged PRs (30d)
- 1
Description
I am copying the text from rust-lang/rust#10520 which was closed as part of triage:
Now that the swap operator has been removed, I do not believe the
restriction against mutating LV is needed, and in fact it prevents
some useful patterns. For example, the following function will
fail to compile:
fn mut_shift_ref<'a,T>(x: &mut &'a mut [T]) -> &'a mut T {
// `mut_split` will restrict mutation against *x:
let (head, tail) = (*x).mut_split(1);
// Hence mutating `*x` yields an error here:
*x = tail;
&mut head[0]
}
Note that this function -- which adjusts the slice *x in place so
that it no longer contains the head element and then returns a
pointer to that element separately -- is perfectly valid. It is
currently implemented using unsafe code. I believe that now that
the swap operator is removed from the language, we could liberalize
the rules and make this function be accepted normally. The idea
would be to have the assignment to *x kill the loans of *x and
its subpaths -- after all, those subpaths are no longer accessible
through *x, since it has been overwritten with a new value. Thus
those subpaths are only accessible through prior existing borrows
of *x, if any. The danger of the swap operator was that it
allowed *x to be mutated without making the subpaths of *x
inaccessible: worse, they became accessible through a new path (I
suppose that we could have supported a swap operator, too, if needed, by moving the loans over to the new path).
And in a later comment:
This may have become more important. As part of https://github.com/rust-lang/rust/pull/20341, we started doing coercions on assignments of the form:
x = y
If x has type &mut X, then this causes a reborrow of y whereas it used to cause a move. Unfortunately, this can break code that used to work. Here is a reduced example:
fn foo(mut vec: Vec<int>) {
let mut iter = vec.iter_mut();
let mut cur = iter.next().unwrap();
let mut next = iter.next().unwrap();
loop {
*next = 22; // ERROR cannot assign to `next` because it is borrowed
cur = next; // NOTE borrowed here
next = iter.next().unwrap();
}
}
fn main() {}
This can be worked around by forcing a move using a dummy newtype:
struct Dummy<T>(T);
fn foo(mut vec: Vec<isize>) {
let mut iter = vec.iter_mut();
let mut cur = Dummy(iter.next().unwrap());
let mut next = iter.next().unwrap();
loop {
*next = 22;
cur = Dummy(next);
next = iter.next().unwrap();
}
}
fn main() {}
But that's awful!
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the mut_shift_ref and foo examples in the issue, then read the linked rust-lang/rust#10520 discussion and the assignment-coercion context from PR #20341. Define how assignment should invalidate loans while preserving prior borrows, and validate the behavior against both examples, including the cited red_black.rs case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100