struct S {
x: String,
y: String
}
fn x(s: &mut S) {
let mut c = move || {
// a `&mut` access to `(*s).x`
// ^ passed through the deref of an `&mut` value
s.x.truncate(0);
};
c();
}
struct S {
x: &mut String,
y: String
}
fn x(s: S) {
let mut c = move || {
// a `&mut` access to `*(s.x)`
// ^
// we would want to capture `&mut *s.x`
s.x.truncate(0);
};
c();
}
Interesting corner cases:
&mut
access to deref of shared borrow
let mut s = String::new();
let t = (&mut s, 10);
let c = || {
// We see that a capture in its entirety is being
// modfied
// i.e. we don't build a place on top of it
// Therefore the capture is mutable if the root variable of the capture is mutable
*t.0 = format!("Some other string");
}
let c = || s.x.truncate(0); // `&mut` access to `(*s).x`, so you get a mutable ref mode for the upvar
let c = move || s.x.truncate(0); // without this, you would move `s` because you stop at the deref
let c = || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.x); // (error)
};
struct S { x: String, y: String }
fn foo(s: &mut S) {
// Closure will:
// * move `s` because we don't move through derefs, so we can't move `s.y
let c = || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.y); // (error)
};
}
struct S { x: &mut String, y: &mut String }
fn foo(s: S) {
// Closure will:
// * mut-borrow `*s.x`
// * move `s.y`
// but this doesn't require our special code.
let c = || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.y); // (error)
};
}
struct S { x: String, y: &mut String }
fn foo(s: Box<S>) {
// Closure will:
// * move `s`
let c = || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.y); // (error)
};
}
struct S { x: String, y: &mut String }
fn foo(s: S) {
// Closure will:
// * move `s`
let c = || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.x.something);
};
}
what is the reborrowing change:
if you are in a move closure,
&mut
reference,if you are in a move closure,
&
reference,two phases for the analysis
S
of accesses {(Mode, Place)}
where:
Mode
is by-value
, ref
, or mut-ref
Place
is a placeS
and produces a set of S' = {(Mode, Place)}
of capturesfirst part, computing the set S
of accesses, before reborrows:
(Move, P)
(M, P)
first part, computing the set S
of accesses, after reborrows:
&mut
, add (MutRef, P)
to S
&
, add (Ref, P)
to S
(Move, P)
(M, P)
second part, min-capture-analysis will reconcile accesses to overlapping paths:
P
and an access to some suffix P.Q
, we prefer P
struct S { x: &mut String, y: &mut String }
fn foo(s: S) {
// Closure will:
// * move `s`
let c = move || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.x.something);
};
// The set S is (MutRef, *s.x), (Move, (*s.x).something)
// Min-capture-analysis is going to truncate the move to `s.x`
// we can drop the `(MutRef, *s.x)` access as redundant
// So we wind up capturing `s.x` by move
}
struct S { x: &mut String, y: &mut String }
fn foo(s: S) {
// Closure will:
// * move `s`
let c = move || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.y.something);
};
// The set S is (MutRef, *s.x), (Move, (*s.y).something)
// Min-capture-analysis is going to truncate the move to `s.y`
// So we wind up capturing `*s.x` by mut ref, s.y by move
}
struct S { x: String, y: String }
fn foo(s: &mut S) {
// Closure will:
// * move `s`
let c = move || {
s.x.truncate(0); // without this, you would move `s` because you stop at the deref
drop(s.y);
};
// The set S is (MutRef, (*s).x), (Move, (*s).y)
// Min-capture-analysis is going to truncate the move to `s`, mut-ref is redundant
// So we wind up capturing `s` by move
}
struct S(i32);
// E0
fn foo() {
let b = Box::new(S(10));
let c = #[rustc_capture_analysis] move || {
let _b = b.0; // consume *b.0, Copy
};
c();
}
&T
, only a deref of a Box
(*b).0
by ref – no, this is falseb
b
by moveIf you wanted to not move b
, would have to remove move
or do:
struct S(i32);
// E1
fn foo() {
let b = Box::new(S(10));
let b1 = &b;
let c = #[rustc_capture_analysis] move || {
let _b = b1.0; // consume *b.0, Copy
};
c();
}