I read the RFC about NLL. I know it's an internal thing, and I want to understand how rust can tell the below code violates the borrowing rule through MIR, instead of lexical scope.
fn capitalize(_: &mut [char]) {}
fn bar() {
let mut data = vec!['a', 'b', 'c'];
let slice = &mut data[..]; // <-+ 'lifetime
data.push('d'); // ERROR! // |
capitalize(slice); // |
} // <------------------------------+
Motivation
understanding borrowing rules: reborrow and NLL makes it hard to understand borrowing rules.
The lifetime can be defined in finer-grained regions
in terms of MIR.
This RFC proposes a more flexible model for lifetimes. Whereas previously lifetimes were based on the abstract syntax tree, we now propose lifetimes that are defined via the control-flow graph. More specifically, lifetimes will be derived based on the MIR used internally in the compiler.
Context
The given example in the RFC:
fn foo() {
let mut data = vec!['a', 'b', 'c']; // --+ 'scope
capitalize(&mut data[..]); // |
// ^~~~~~~~~~~~~~~~~~~~~~~~~ 'lifetime // |
data.push('d'); // |
data.push('e'); // |
data.push('f'); // |
} // <---------------------------------------+
fn capitalize(data: &mut [char]) {
// do something
}
pseudo-MIR
let mut data: Vec<i32>;
let slice: &'slice mut i32;
START {
data = ...;
slice = &'borrow mut data;
capitalize(slice);
data.push('d');
data.push('e');
data.push('f');
}
The constraints generated will be as follows:
('slice: {START/2}) @ START/2
('borrow: 'slice) @ START/2
My understanding is the lifetime of 'borrow
is only at point START/2
;
Question
what about below? Just move data.push
ahead of capitalize
:
let mut data: Vec<i32>;
let slice: &'slice mut [char];
let tmp0: &'tmp0 mut Vec<i32>;
START {
/*0*/ data = ...;
/*1*/ slice = &'borrow mut data[..];
/*2*/ ... // some dummy code, not using slice nor tmp0
/*3*/ tmp0 = &'tmp1 mut data;
/*4*/ Vec::push(tmp0, 'd');
/*5*/ capitalize(slice);
}
My intuition tells me, borrow
should have scope from START/1 to START/5, Within borrow
, there shouldn't exist any mutable reference to data.
But following the RFC, it seems, there's no overlap of 'tmp1
and 'borrow
,
tmp1: {START/4}
borrow: {START/2, START/5}
There's no multiple mutable access to data
and it should compile?
But it doesn't. playground
error[E0499]: cannot borrow `data` as mutable more than once at a time
--> src/lib.rs:6:5
|
5 | let slice = &mut data[..]; // <-+ 'lifetime
| ---- first mutable borrow occurs here
6 | data.push('d'); // ERROR! // |
| ^^^^ second mutable borrow occurs here
7 | capitalize(slice); // |
| ----- first borrow later used here
Could anyone tell me where I did wrong in following the algorithm proposed in that RFC?