Module ecstatic::join[][src]

Expand description

Support for iterating over a joined view of different components.

Soundness

This part of the library contains some unsafe code, which could set of some alarm bells. However, this usage should be safe and free of undefined behavior.

Consider the following:

type Dependencies = (ReadComponent<'a, A>,
                     WriteComponent<'a, B>,
                     WriteComponent<'a, C>);
let (a, b, c,): Dependencies = ...;
(&a, &b, &mut c,).for_each(|(va, vb, vc)| { ... });

Through ~trait magic~ this is implemented as:

for e in ... {
    (&a, (&b, (&mut c, ()))).process(e, |v| f(v.flatten()));
}

process() builds up the nested (&A, (&B, (&mut C, ()))) tuple recursively. Since it requires &mut self, we know that we have exclusive access to the tuple of Read/Write specifiers, and therefore exclusive access to the underlying component storage. Additionally, we know that we can’t have multiple mutable references to the same storage in the nested list, because that list is constructed entirely in safe Rust.

This call to process then recursively becomes:

let v = a.get_raw(e)
(&b, (&mut c, ())).process(e, move |tail| f((&*v, tail).flatten()));

And so on, until we reach the () list terminator.

Moving a safe reference into a closure in the manner described above requires that the reference be borrowed for 'static. Similarly, if we were to store the capture ourselves in a struct, that reference would need to outlive the enclosing scope’s lifetime, in the general case. The compiler apparently cannot prove that the closure does not outlive the recursive chain of process() calls, so this causes the borrow checker to report a conflict. Since we are not storing the closure anywhere, we know we don’t actually need the reference to live longer than the call, so we can move the reference into the closure as a raw pointer to work around the borrow checker.

While technically unsafe, this doesn’t violate any aliasing or borrow rules; the borrows have been checked in the safe layer, and the unsafe layer doesn’t create any aliases or references that outlive those borrows.

Furthermore, it is impossible for client code (via the closure passed to for_each) to violate the soundness of this approach in safe Rust, since the references passed to the closure are only borrowed for the duration of that call. Joinable is also a sealed trait, so it is not possible for client code to violate this soundness by implementing this trait and doing something funky with the closure.

Traits

Join

Trait for joining different component types together.

Joinable

Indicates that the type can be joined via the Join api.