Files
dioxus/packages/core/src/contextapi.rs
T
Jonathan Kelley 204f0d9f16 Feat:
- integrate subscription service into context.
- Update documentation
2021-02-08 16:57:34 -05:00

88 lines
3.8 KiB
Rust

//! Context API
//!
//! The context API provides a mechanism for components to grab
//!
//!
//!
use std::marker::PhantomPinned;
/// Any item that works with app
pub trait AppContext {}
#[derive(Copy, Clone)]
pub struct ContextGuard<'a, T> {
inner: *mut T,
_p: std::marker::PhantomData<&'a ()>,
}
impl<'a, PropType> super::context::Context<'a, PropType> {
/// # SAFETY ALERT
///
/// The underlying context mechanism relies on mutating &mut T while &T is held by components in the tree.
/// By definition, this is UB. Therefore, implementing use_context should be done with upmost care to invalidate and
/// prevent any code where &T is still being held after &mut T has been taken and T has been mutated.
///
/// While mutating &mut T while &T is captured by listeners, we can do any of:
/// 1) Prevent those listeners from being called and avoid "producing" UB values
/// 2) Delete instances of closures where &T is captured before &mut T is taken
/// 3) Make clones of T to preserve the original &T.
///
/// To guarantee safe usage of state management solutions, we provide Dioxus-Reducer and Dioxus-Dataflow built on the
/// SafeContext API. This should provide as an example of how to implement context safely for 3rd party state management.
///
/// It's important to recognize that while safety is a top concern for Dioxus, ergonomics do take prescendence.
/// Contrasting with the JS ecosystem, Rust is faster, but actually "less safe". JS is, by default, a "safe" language.
/// However, it does not protect you against data races: the primary concern for 3rd party implementers of Context.
///
/// We guarantee that any &T will remain consistent throughout the life of the Virtual Dom and that
/// &T is owned by components owned by the VirtualDom. Therefore, it is impossible for &T to:
/// - be dangling or unaligned
/// - produce an invalid value
/// - produce uninitialized memory
///
/// The only UB that is left to the implementer to prevent are Data Races.
///
/// Here's a strategy that is UB:
/// 1. &T is handed out via use_context
/// 2. an event is reduced against the state
/// 3. An &mut T is taken
/// 4. &mut T is mutated.
///
/// Now, any closures that caputed &T are subject to a data race where they might have skipped checks and UB
/// *will* affect the program.
///
/// Here's a strategy that's not UB (implemented by SafeContext):
/// 1. ContextGuard<T> is handed out via use_context.
/// 2. An event is reduced against the state.
/// 3. The state is cloned.
/// 4. All subfield selectors are evaluated and then diffed with the original.
/// 5. Fields that have changed have their ContextGuard poisoned, revoking their ability to take &T.a.
/// 6. The affected fields of Context are mutated.
/// 7. Scopes with poisoned guards are regenerated so they can take &T.a again, calling their lifecycle.
///
/// In essence, we've built a "partial borrowing" mechanism for Context objects.
///
/// =================
/// nb
/// =================
/// If you want to build a state management API directly and deal with all the unsafe and UB, we provide
/// `use_context_unchecked` with all the stability with *no* guarantess of Data Race protection. You're on
/// your own to not affect user applications.
///
/// - Dioxus reducer is built on the safe API and provides a useful but slightly limited API.
/// - Dioxus Dataflow is built on the unsafe API and provides an even snazzier API than Dioxus Reducer.
pub fn use_context<C: AppContext>(&'a self) -> C {
todo!()
}
pub unsafe fn use_context_unchecked<C: AppContext>() {}
}
struct SafeContext<T> {
value: T,
// This context is pinned
_pinned: PhantomPinned,
}