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.
§Passes
The Pass is a sort of “key” for accessing the value within an
RwData, it’s purpose is to maintain Rust’s number one rule,
i.e. one exclusive reference or multiple shared references, and
that is done by borrowing the Pass mutably or non mutably.
That comes with some limitations, of course, mainly that you can’t
really mutate two RwDatas at the same time, even if it is
known that they don’t point to the same data.
There are some common exceptions to this, where Duat provides some safe way to do that when it is known that the two types are not the same.
§Not Send/Sync
Internally, the RwData makes use of an Rc<RefCell>. The
usage of an Rc<RefCell> over an Arc<Mutex> is, i’ve
assumed, a necessary evil in order to preserve the aforementioned
rule. But the lack of Send/Sync does confer the RwData
some advantages:
- Deadlocks are impossible, being replaced by much easier to debug panics.
- The order in which data is accessed doesn’t matter, unlike with
Mutexes. - Performance of unlocking and cloning should generally be better,
since no atomic operations are done (I actually managed to
observe this, where in my rudimentary benchmarks against neovim,
the
Arc<Mutex>version was very frequently losing to a comparable neovim build.
However, I admit that there are also some drawbacks, the most
notable being the difficulty of reading or writing to Text
from outside of the main thread. But for the most common usecase
where that will be needed (Readers), a Send/Sync
solution will be provided soon.
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.
Sourcepub fn replace(&self, pa: &mut Pass, new: T) -> T
pub fn replace(&self, pa: &mut Pass, new: T) -> T
Replaces 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.
§Panics
Panics if there is any type of borrow of this struct
somewhere, which could happen if you use read_unsafe
or write_unsafe, for example.
Sourcepub fn get(&self, pa: &Pass) -> Twhere
T: Clone,
pub fn get(&self, pa: &Pass) -> Twhere
T: Clone,
Gets 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.
§Panics
Panics if there is a mutable borrow of this struct somewhere,
which could happen if you use write_unsafe or
write_unsafe_as
Source§impl<T: ?Sized> RwData<T>
impl<T: ?Sized> RwData<T>
Sourcepub unsafe fn new_unsized<SizedT: 'static>(value: Rc<RefCell<T>>) -> Self
pub unsafe fn new_unsized<SizedT: 'static>(value: Rc<RefCell<T>>) -> Self
Returns an unsized RwData, such as RwData<dyn Trait>
§Safety
There is a type argument SizedT which must be the exact
type you are passing to this constructor, i.e., if you are
creating an RwData<dyn Display> from a String, you’d
do this:
use std::{cell::RefCell, fmt::Display, rc::Rc};
use duat_core::{data::RwData, prelude::*};
let rw_data: RwData<dyn Display> =
unsafe { RwData::new_unsized::<String>(Rc::new(RefCell::new("testing".to_string()))) };This ensures that methods such as read_as and write_as
will correctly identify such RwData<dyn Display> as a
String.
Sourcepub fn read<Ret>(&self, _: &Pass, f: impl FnOnce(&T) -> Ret) -> Ret
pub fn read<Ret>(&self, _: &Pass, f: impl FnOnce(&T) -> Ret) -> Ret
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.
§Panics
Panics if there is a mutable borrow of this struct somewhere,
which could happen if you use write_unsafe or
write_unsafe_as
Sourcepub fn read_as<Ret, U: 'static>(
&self,
_: &Pass,
f: impl FnOnce(&U) -> Ret,
) -> Option<Ret>
pub fn read_as<Ret, U: 'static>( &self, _: &Pass, f: impl FnOnce(&U) -> Ret, ) -> Option<Ret>
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.
§Panics
Panics if there is a mutable borrow of this struct somewhere,
which could happen if you use write_unsafe or
write_unsafe_as
Sourcepub unsafe fn read_unsafe<Ret>(&self, f: impl FnOnce(&T) -> Ret) -> Ret
pub unsafe fn read_unsafe<Ret>(&self, f: impl FnOnce(&T) -> Ret) -> Ret
Reads the value within, without a Pass
While a lack of Passes grants you more freedom, it may
also cause panics if not handled carefully, since you could be
breaking the number one rule of Rust.
§Panics
Panics if there is a mutable borrow of this struct somewhere
§Safety
In order to safely use this function without panicking, there are some useful guidelines that you should follow:
- The value being read does not have any
RwDatawithin; - You know that this value is not being shared anywhere else;
Essentially, in order to use this safely, you should treat it
like a glorified RefCell
Sourcepub unsafe fn read_unsafe_as<Ret, U: 'static>(
&self,
f: impl FnOnce(&U) -> Ret,
) -> Option<Ret>
pub unsafe fn read_unsafe_as<Ret, U: 'static>( &self, f: impl FnOnce(&U) -> Ret, ) -> Option<Ret>
Reads the value within as U, without a Pass
While a lack of Passes grants you more freedom, it may
also cause panics if not handled carefully, since you could be
breaking the number one rule of Rust.
§Panics
Panics if there is a mutable borrow of this struct somewhere
§Safety
In order to safely use this function without panicking, there are some useful guidelines that you should follow:
- The value being read does not have any
RwDatawithin; - You know that this value is not being shared anywhere else;
Essentially, in order to use this safely, you should treat it
like a glorified RefCell
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<Ret>(&self, _: &mut Pass, f: impl FnOnce(&mut T) -> Ret) -> Ret
pub fn write<Ret>(&self, _: &mut Pass, f: impl FnOnce(&mut T) -> Ret) -> Ret
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.
§Panics
Panics if there is any type of borrow of this struct
somewhere, which could happen if you use read_unsafe
or write_unsafe, for example.
Sourcepub fn write_as<Ret, U: 'static>(
&self,
_: &mut Pass,
f: impl FnOnce(&mut U) -> Ret,
) -> Option<Ret>
pub fn write_as<Ret, U: 'static>( &self, _: &mut Pass, f: impl FnOnce(&mut U) -> Ret, ) -> Option<Ret>
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.
§Panics
Panics if there is any type of borrow of this struct
somewhere, which could happen if you use read_unsafe
or write_unsafe, for example.
Sourcepub unsafe fn write_unsafe<Ret>(&self, f: impl FnOnce(&mut T) -> Ret) -> Ret
pub unsafe fn write_unsafe<Ret>(&self, f: impl FnOnce(&mut T) -> Ret) -> Ret
Writes to the value within as U, without a Pass
While a lack of Passes grants you more freedom, it may
also cause panics if not handled carefully, since you could be
breaking the number one rule of Rust.
§Panics
Will panic if there are any types of borrows of the value within somewhere else.
§Safety
In order to safely use this function without panicking, there are some useful guidelines that you should follow:
- The value being read does not have any
RwDatawithin; - You know that this value is not being shared anywhere else;
Essentially, in order to use this safely, you should treat it
like a glorified RefCell
Sourcepub unsafe fn write_unsafe_as<Ret, U: 'static>(
&self,
f: impl FnOnce(&mut U) -> Ret,
) -> Option<Ret>
pub unsafe fn write_unsafe_as<Ret, U: 'static>( &self, f: impl FnOnce(&mut U) -> Ret, ) -> Option<Ret>
Writes to the value within, without a Pass
While a lack of Passes grants you more freedom, it may
also cause panics if not handled carefully, since you could be
breaking the number one rule of Rust.
§Panics
Will panic if there are any types of borrows of the value within somewhere else.
§Safety
In order to safely use this function without panicking, there are some useful guidelines that you should follow:
- The value being read does not have any
RwDatawithin; - You know that this value is not being shared anywhere else;
Essentially, in order to use this safely, you should treat it
like a glorified RefCell
Sourcepub fn declare_written(&self)
pub fn declare_written(&self)
Simulates a write without actually reading
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 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 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 was of type
U, i.e., for unsized types, U was 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.