Struct RwData

Source
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>

Source

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

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.

Source

pub fn get(&self, pa: &Pass) -> T
where 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>

Source

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.

Source

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

Source

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

Source

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 RwData within;
  • 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

Source

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 RwData within;
  • 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

Source

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.

Source

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.

Source

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.

Source

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 RwData within;
  • 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

Source

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 RwData within;
  • 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

Source

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.

Source

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.

Source

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.

Source

pub fn ptr_eq<U: ?Sized>(&self, other: &RwData<U>) -> bool

Wether this RwData and another point to the same value

Source

pub fn type_id(&self) -> TypeId

The TypeId of the concrete type within

Source

pub fn data_is<U: 'static>(&self) -> bool

Wether the concrete TypeId matches that of U

Source

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

pub fn checker(&self) -> impl Fn() -> bool + 'static

A function that checks if the data has been updated

Do note that this function will check for the specific RwData that was used in its creation, so if you call read on that specific RwData for example, this function will start returning false.

Source

pub fn clone_text<U>(&self, pa: &Pass) -> Text
where T: Widget<U>, U: Ui,

Clones the Text of the Widget

Source

pub fn take_text<U>(&self, pa: &mut Pass) -> Text
where T: Widget<U>, U: Ui,

Takes the Text from the Widget, replacing it with the Default

Source

pub fn replace_text<U>(&self, pa: &mut Pass, text: impl Into<Text>) -> Text
where T: Widget<U>, U: Ui,

Replaces the Text of the Widget, returning the previous value

Source

pub fn print_cfg<U>(&self, pa: &Pass) -> PrintCfg
where T: Widget<U>, U: Ui,

The PrintCfg of the Widget

Source

pub fn needs_update<U>(&self, pa: &Pass) -> bool
where T: Widget<U>, U: Ui,

Whether the Widget needs to be updated

Trait Implementations§

Source§

impl<T: ?Sized> Clone for RwData<T>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

const fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug + ?Sized> Debug for RwData<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Default + 'static> Default for RwData<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<T> Freeze for RwData<T>
where T: ?Sized,

§

impl<T> !RefUnwindSafe for RwData<T>

§

impl<T> !Send for RwData<T>

§

impl<T> !Sync for RwData<T>

§

impl<T> Unpin for RwData<T>
where T: ?Sized,

§

impl<T> !UnwindSafe for RwData<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.