Add safe constructor for EventLoopRuntime

Change-Id: I32766a1aa693795db2aa223ebdb8b0ae84a9af29
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/events/event_loop_runtime.rs b/aos/events/event_loop_runtime.rs
index dfd206e..c79b97a 100644
--- a/aos/events/event_loop_runtime.rs
+++ b/aos/events/event_loop_runtime.rs
@@ -238,23 +238,9 @@
     where
         F: for<'event_loop> FnOnce(EventLoopRuntime<'event_loop>),
     {
-        // SAFETY: The EventLoopRuntime never escapes this function, which means the only code that
-        // observes its lifetime is `fun`. `fun` must be generic across any value of its
-        // `'event_loop` lifetime parameter, which means we can choose any lifetime here, which
-        // satisfies the safety requirements.
-        //
-        // This is a similar pattern as `std::thread::scope`, `ghost-cell`, etc. Note that unlike
-        // `std::thread::scope`, our inner functions (the async ones) are definitely not allowed to
-        // capture things from the calling scope of this function, so there's no `'env` equivalent.
-        // `ghost-cell` ends up looking very similar despite doing different things with the
-        // pattern, while `std::thread::scope` has a lot of additional complexity to achieve a
-        // similar result.
-        //
-        // `EventLoopHolder`s safety requirements prevent anybody else from touching the underlying
-        // `aos::EventLoop`.
+        // SAFETY: The event loop pointer produced by into_raw must be valid.
         let cpp_runtime = unsafe { CppEventLoopRuntime::new(event_loop.into_raw()).within_box() };
-        let runtime = unsafe { EventLoopRuntime::new(&cpp_runtime) };
-        fun(runtime);
+        EventLoopRuntime::with(&cpp_runtime, fun);
         Self(ManuallyDrop::new(cpp_runtime), PhantomData)
     }
 }
@@ -271,6 +257,9 @@
     }
 }
 
+/// Manages the Rust interface to a *single* `aos::EventLoop`.
+///
+/// This is intended to be used by a single application.
 #[derive(Copy, Clone)]
 pub struct EventLoopRuntime<'event_loop>(
     &'event_loop CppEventLoopRuntime,
@@ -278,13 +267,12 @@
     InvariantLifetime<'event_loop>,
 );
 
-/// Manages the Rust interface to a *single* `aos::EventLoop`. This is intended to be used by a
-/// single application.
 impl<'event_loop> EventLoopRuntime<'event_loop> {
-    /// Creates a new runtime. This must be the only user of the underlying `aos::EventLoop`.
+    /// Creates a new runtime for the underlying event loop.
     ///
     /// Consider using [`EventLoopRuntimeHolder.new`] instead, if you're working with an
-    /// `aos::EventLoop` owned (indirectly) by Rust code.
+    /// `aos::EventLoop` owned (indirectly) by Rust code or using [`EventLoopRuntime::with`] as a safe
+    /// alternative.
     ///
     /// One common pattern is calling this in the constructor of an object whose lifetime is managed
     /// by C++; C++ doesn't inherit the Rust lifetime but we do have a lot of C++ code that obeys
@@ -368,6 +356,26 @@
         Self(event_loop, InvariantLifetime::default())
     }
 
+    /// Safely builds a "constrained" EventLoopRuntime with `fun`.
+    ///
+    /// We constrain the scope of the `[EventLoopRuntime]` by tying it to **any** `'a` lifetime. The
+    /// idea is that the only things that satisfy this lifetime are either ``static` or produced by
+    /// the event loop itself with a '`event_loop` runtime.
+    pub fn with<F>(event_loop: &'event_loop CppEventLoopRuntime, fun: F)
+    where
+        F: for<'a> FnOnce(EventLoopRuntime<'a>),
+    {
+        // SAFETY: We satisfy the event loop lifetime constraint by scoping it inside of a higher-
+        // rank lifetime in FnOnce. This is similar to what is done in std::thread::scope, and the
+        // point is that `fun` can only assume that `'static` and types produced by this typewith a
+        // 'event_loop lifetime are the only lifetimes that will satisfy `'a`. This is possible due
+        // to this type's invariance over its lifetime, otherwise, one could easily make a Subtype
+        // that, due to its shorter lifetime, would include things from its outer scope.
+        unsafe {
+            fun(Self::new(event_loop));
+        }
+    }
+
     /// Returns the pointer passed into the constructor.
     ///
     /// The returned value should only be used for destroying it (_after_ `self` is dropped) or