pub struct RwData<T: ?Sized> { /* private fields */ }Expand description
A container for shared read/write state
This is the struct used internally (and externally) to allow for
massively shareable state in duat’s API. Its main purpose is to
hold all of the Widgets in Duat, making them available for
usage from any function with access to a Pass. However, it can
also be used to hold any other type, and also has the ability to
notify when changes have taken place.
§Passes
The Pass is a sort of “key” for accessing the value within an
RwData, its purpose is to maintain Rust’s number one rule,
i.e. one exclusive reference or multiple shared references
(mutability XOR aliasing), and that is done by borrowing the
Pass mutably or non mutably. That comes with some limitations
on how they can be used, mostly the fact that you must mutably
borrow all RwDatas that will be used at the same time in
order to get multiple mutable references at once.
The use of a Pass for reading/writing to RwDatas confers
various benefits:
- Aside from updating an internal update counter, it is truly
zero cost, unlike in the case of a
MutexorRefCell, which have to do checks at runtime. This happens because thePasshas zero size, i.e. it gets removed at compile time. - You can’t run into deadlocks like you would be able to when
using
Mutexes. Neither can you run into panics from reborrowing, like withRefCell. - You don’t have to drop a
Guardtype (likeMutexGuard) in order to reborrow from theRwDatasince borrowing gives you a first class&or&mut, which are much easier to work with. - You can do sub borrowings, like
&mut data.write(pa).field, given the&mutborrow.
However, there are also a few disadvantages:
- Sometimes, mutably borrowing multiple things in a single
function can be a challenge, although that is mostly mitigated
by
Pass::write_many. - You cannot access the data in a
RwDatafrom threads other than the main one, since thePassis only accessible from it. This isn’t really a disadvantage, since it simplifies thought patterns and eases reasoning about the current state of things.
Implementations§
Source§impl<T: 'static> RwData<T>
impl<T: 'static> RwData<T>
Sourcepub fn new(value: T) -> Self
pub fn new(value: T) -> Self
Returns a new RwData<T>
Note that this is only for sized types. For unsized types, the
process is a little more convoluted, and you need to use
RwData::new_unsized.
Source§impl<T: ?Sized> RwData<T>
impl<T: ?Sized> RwData<T>
Sourcepub fn read<'p>(&'p self, _: &'p Pass) -> &'p T
pub fn read<'p>(&'p self, _: &'p Pass) -> &'p T
Reads the value within using a Pass
The consistent use of a Pass for the purposes of
reading/writing to the values of RwDatas ensures that no
panic or invalid borrow happens at runtime, even while working
with untrusted code. More importantly, Duat uses these
guarantees in order to give the end user a ridiculous amount
of freedom in where they can do things, whilst keeping Rust’s
number one rule and ensuring thread safety, even with a
relatively large amount of shareable state.
Sourcepub fn read_as<'p, U: 'static>(&'p self, _: &'p Pass) -> Option<&'p U>
pub fn read_as<'p, U: 'static>(&'p self, _: &'p Pass) -> Option<&'p U>
Reads the value within as U using a Pass
The consistent use of a Pass for the purposes of
reading/writing to the values of RwDatas ensures that no
panic or invalid borrow happens at runtime, even while working
with untrusted code. More importantly, Duat uses these
guarantees in order to give the end user a ridiculous amount
of freedom in where they can do things, whilst keeping Rust’s
number one rule and ensuring thread safety, even with a
relatively large amount of shareable state.
Sourcepub fn declare_as_read(&self)
pub fn declare_as_read(&self)
Simulates a read without actually reading
This is useful if you want to tell Duat that you don’t want
has_changed to return true, but you don’t have a
Pass available to read the value.
Sourcepub fn write<'p>(&'p self, _: &'p mut Pass) -> &'p mut T
pub fn write<'p>(&'p self, _: &'p mut Pass) -> &'p mut T
Writes to the value within using a Pass
The consistent use of a Pass for the purposes of
reading/writing to the values of RwDatas ensures that no
panic or invalid borrow happens at runtime, even while working
with untrusted code. More importantly, Duat uses these
guarantees in order to give the end user a ridiculous amount
of freedom in where they can do things, whilst keeping Rust’s
number one rule and ensuring thread safety, even with a
relatively large amount of shareable state.
Sourcepub fn write_as<'p, U: 'static>(&'p self, _: &'p mut Pass) -> Option<&'p mut U>
pub fn write_as<'p, U: 'static>(&'p self, _: &'p mut Pass) -> Option<&'p mut U>
Writes to the value within as U using a Pass
The consistent use of a Pass for the purposes of
reading/writing to the values of RwDatas ensures that no
panic or invalid borrow happens at runtime, even while working
with untrusted code. More importantly, Duat uses these
guarantees in order to give the end user a ridiculous amount
of freedom in where they can do things, whilst keeping Rust’s
number one rule and ensuring thread safety, even with a
relatively large amount of shareable state.
Sourcepub fn write_then<'p, Tup: WriteableTuple<'p, impl Any>>(
&'p self,
pa: &'p mut Pass,
tup_fn: impl FnOnce(&'p T) -> Tup,
) -> (&'p mut T, Tup::Return)
pub fn write_then<'p, Tup: WriteableTuple<'p, impl Any>>( &'p self, pa: &'p mut Pass, tup_fn: impl FnOnce(&'p T) -> Tup, ) -> (&'p mut T, Tup::Return)
Writes to the value and internal RwData-like structs
This method takes a function that borrows a [WriteableTuple]
from self, letting you write to self and the data in the
tuple (or single element) at the same time.
This is really useful in a scenario where, for example, your
Handle<W> for some widget W holds a Handle<Buffer>,
and you wish to access both at the same time, while writing to
the former:
use duat::prelude::*;
struct MyWidget {
text: Text,
buf: Handle,
}
impl Widget for MyWidget {
fn update(pa: &mut Pass, handle: &Handle<Self>) {
let (wid, buf) = handle.write_then(pa, |wid| &wid.buf);
// Updating the widget and reading/writing from the Buffer at the same time.
// ...
}
// ..
}You can also return tuples from the function, allowing for
access to up to twelve different RwData-like structs:
use duat::prelude::*;
struct MyWidget {
text: Text,
buf1: Handle,
buf2: Handle,
}
impl Widget for MyWidget {
fn update(pa: &mut Pass, handle: &Handle<Self>) {
let (wid, (b1, b2)) = handle.write_then(pa, |wid| (&wid.buf1, &wid.buf2));
// ...
}
// ..
}§Panics
This function will panic if any of the elements of the tuple
point to the same data as any other element or self, see
Pass::write_many for more information.
Sourcepub fn declare_written(&self)
pub fn declare_written(&self)
Simulates a write without actually writing
This is useful if you want to tell Duat that you want
has_changed to return true, but you don’t have a
Pass available to write the value with.
Sourcepub fn take(&self, pa: &mut Pass) -> Twhere
T: Default,
pub fn take(&self, pa: &mut Pass) -> Twhere
T: Default,
Takes the value within, replacing it with the default
Sourcepub fn map<Ret: 'static>(
&self,
map: impl FnMut(&T) -> Ret + 'static,
) -> DataMap<T, Ret>
pub fn map<Ret: 'static>( &self, map: impl FnMut(&T) -> Ret + 'static, ) -> DataMap<T, Ret>
Maps the value to another value with a function
This function will return a struct that acts like a “read
only” version of RwData, which also maps the value to
a return type.
Sourcepub fn map_mut<Ret: 'static>(
&self,
map: impl FnMut(&mut T) -> Ret + 'static,
) -> MutDataMap<T, Ret>
pub fn map_mut<Ret: 'static>( &self, map: impl FnMut(&mut T) -> Ret + 'static, ) -> MutDataMap<T, Ret>
Maps the value to another value with a mutating function
This is useful if you want to repeat a function over and over again in order to get a new different result, whilst mutating the data within.
Sourcepub fn try_downcast<U: 'static>(&self) -> Option<RwData<U>>
pub fn try_downcast<U: 'static>(&self) -> Option<RwData<U>>
Attempts to downcast an RwData to a concrete type
Returns Some(RwData<U>) if the value within is of type
U. For unsized types, U is the type parameter
passed when calling RwData::new_unsized.
Sourcepub fn ptr_eq<U: ?Sized>(&self, other: &RwData<U>) -> bool
pub fn ptr_eq<U: ?Sized>(&self, other: &RwData<U>) -> bool
Wether this RwData and another point to the same value
Sourcepub fn has_changed(&self) -> bool
pub fn has_changed(&self) -> bool
Wether someone else called write or write_as since the
last read or write
Do note that this DOES NOT mean that the value inside has
actually been changed, it just means a mutable reference was
acquired after the last call to has_changed.
Some types like Text, and traits like Widget offer
has_changed methods,
you should try to determine what parts to look for changes.
Generally though, you can use this method to gauge that.
Source§impl<W: Widget> RwData<W>
impl<W: Widget> RwData<W>
Sourcepub fn to_dyn_widget(&self) -> RwData<dyn Widget>
pub fn to_dyn_widget(&self) -> RwData<dyn Widget>
Downcasts RwData<impl Widget> to RwData<dyn Widget>
Trait Implementations§
impl<T: ?Sized> Send for RwData<T>
impl<T: ?Sized> Sync for RwData<T>
Auto Trait Implementations§
impl<T> Freeze for RwData<T>where
T: ?Sized,
impl<T> !RefUnwindSafe for RwData<T>
impl<T> Unpin for RwData<T>where
T: ?Sized,
impl<T> !UnwindSafe for RwData<T>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.