Size optimization: Capture root variable

https://github.com/rust-lang/project-rfc-2229/issues/49

Rule #1:

  • For move closures, only copy data that was on the stack frame of closure creator
  • This means: never pass through a dereference, even if the full path leads to a copy type
  • Why?
let x: Box<[u8; 1024]> = ...; let c = move || x.len();

this used to copy 1 pointer, but if we capture *x it would copy 1024 bytes.

Optimization: only copy shared reference

struct MyStruct { a: A, b: B, c: C, } fn foo(m: &MyStruct) { let c = || (&m.a, &m.b); // might as well just capture `m` and not the two fields individually }
// example 1 struct MyStruct<'a> { a: A, b: B, c: C<'a>, } (`(()))
// example 2 struct MyStruct<'a> { a: &'static A, b: B, c: C<'a>, } fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { let c = || drop(&*m.a.field_of_a); // Here we really do want to capture `*m.a` because that outlives `'static` // If we capture `m`, then the closure no longer outlives `'static' // it is constrained to `'a` }

a safe rule would be:

  • If you a variable x of type &T and you borrow a set of paths (*x).field{1...N} you could instead just capture x
    • doesn't match the &*m.a example because
  • Rule:
    • Given a borrowed place P find the "rightmost" dereference D
      • If it is a deref of a shared ref, and all that follows are field accesses
      • You can truncate P to remove the field accesses

Example 1:

  • Borrow (*m).a and borrow (*m).b:
    • truncate to capture m by value (guaranteed to be copy)

Example 2:

  • Access (*(*m).a).field_of_a
    • truncate to (*m).a by value (guaranteed to be copy)

Niko thinks this is a safe optimization

Select a repo