macro_rules! shareable_struct {
(
$(#[$meta:meta])*
$v:vis struct $Struct:ident {
$($fields:tt)*
}
$($actions:tt)*
) => { ... };
}Expand description
Create a struct definition for a global.
The idea is that each field of the struct will be stored in a separate global, and loaded only
when requested. The actions block describes possible ways of using the struct in terms of what
type of access (W or RW) they need to fields of the struct.
The basic syntax is as follows:
dioxus_shareables::shareable_struct! {
pub struct GlobalState {
a: usize = 8,
b: u16 = 12,
c: Vec<u8> = vec![],
}
action A impl pub ATrait = W[a] RW[b]; // Action A with equivalent trait ATrait
pub action B: BType = W[b] RW[a, c]; // Action B with equivalent type BType
action C: pub CType = RW[c]; // Action C with equivalent type CType
action D_ACTION impl pub D = W[c]; // Action D_ACTION with equivalent trait D.
}NOTE: fields in the struct must be Send + Sync
First we declare the struct itself, then “actions” which represent different views of the struct. When we use the struct, we then have to declare which actions we need:
fn Component(cx: Scope) -> Element {
let state = GlobalState::use_(&cx, (A, B)); // Use GlobalState with actions A and B.
// ...
let b = *state.b().read(); // We can access field b because actions B includes it.
//...
}Of course, there’s not a lot of point to grouping shared variables into a type if we don’t implement some methods on the type. This is where the types on the actions come in:
impl GlobalState<CType> {
pub fn c_method(&self) {
// Do some stuff...
}
}
// Valid action markers implement GlobalStateActions:
impl<Actions: GlobalStateActions> GlobalState<Actions> {
// N.B. that D is the trait, not the actions constant:
pub fn clever_d_method(&self) where Actions: D {
let self_ = self.with_actions(D_ACTION); // We probably want to typecast
// at the start of the method.
// ...
}
}
// ...
fn Component(cx: Scope) -> Element {
let a_state = GlobalState::use_(&cx, A);
let b_state = GlobalState::use_(&cx, B);
let c_state = GlobalState::use_(&cx, C);
// a_state.c_method(); // This will fail since `a_state` doesn't doesn't meet the RW[c] requirement.
// b_state.c_method(); // This will fail because the type is wrong.
b_state.as_ref().c_method(); // This works, but only if the type resolves correctly.
b_state.with_actions(C).c_method(); // This is guaranteed to work.
c_state.c_method(); // This works too.
// a_state.clever_d_method(); // Fails because a_state doesn't meet the W[c] requirement.
b_state.clever_d_method(); // This works.
c_state.clever_d_method(); // So does this.
}It’s up to you where you prefer to typecast.
You don’t need to declare actions in advance to use them; in particular, you may want to use one-off action declarations on method declarations:
impl<Actions: GlobalStateActions> GlobalState<Actions> {
pub fn calculate_from_a_and_c(&self) -> usize where Actions:
AsGlobalStateActions<dioxus_shareables::struct_actions!{GlobalState<{RW[a] RW[c]}>}>
{
let self_ = self.with_actions(dioxus_shareables::struct_actions!(GlobalState(RW[a] RW[c])));
self_.a(); // you asked for it, you got it.
// ...
}
}
// ...
fn Component(cx: Scope) -> Element {
let a_state = GlobalState::use_(&cx, A);
let b_state = GlobalState::use_(&cx, B);
// a_state.calculate_from_a_and_c(); // This will fail since `a_state` doesn't meet the RW[c] requirement.
b_state.calculate_from_a_and_c(); // This works, but only if the type resolves correctly.
}If you’d like, you can also organize your shared structure into substructures. A substructure can be included in a larger shared structure by preceding the field name with a pipe like so:
dioxus_shareables::shareable_struct! {
pub struct MoreGlobalState {
u: String = "more global? more state? which is it?!".into(),
v: u32 = 18,
|s: GlobalState,
}
action UVA = W[u] RW[v] |s[A]; // The included actions for s here must be a single
// ident which refers to a declared action for the
// given struct.
action UBC = W[u] |s[B]; // N.B.: The syntax doesn't change if B isn't in scope... B is
// accessed as an associated type of GlobalState.
// If you get errors involving `AssocType` bounds, this is a
// these |s[B] style bounds are the most likely candidtates.
}
// ...
fn Component(cx: Scope) -> Element {
let mgs = MoreGlobalState::use_(&cx, UBC);
mgs.s().clever_d_method(); // Works bcause action our mgs.s() was initialized with the
// `B` action.
// ...
}