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 |