pub struct RwData<T>where
T: ?Sized + 'static,{ /* private fields */ }Expand description
A read-write reference to information, that can tell readers if said information has changed.
The reading part is done through the [ReadableData] trait, as
such, you need to import it in order to read the data of this
struct.
Unlike RoData, this struct can read and write over itself, and
also implements Clone and Into<RoData>, so you can have as
many readers and writers as you want.
All reads and writes are thread safe, allowing Duat to run many
different tasks at once, without letting the program hang from one
particularly slow task (most commonly a slow Widget).
The most common usecase for this data type are Widgets that
read from a FileWidget, such as LineNumbers, which do so
by holding an RoData<FileWidget<U>, which was acquired from an
existing RoData<FileWidget<U>.
struct WidgetThatReadsFromFile<U>
where
U: Ui,
{
file: RoData<FileWidget<U>>,
other_stuff: OtherStuff,
}Internally, all Widgets are stored inside RwDatas, so
access to them is always thread safe and available (read only) to
everyone who wants to read them.
Implementations§
source§impl<T> RwData<T>
impl<T> RwData<T>
sourcepub fn new(data: T) -> Self
pub fn new(data: T) -> Self
Returns a new instance of a RwData<T>, assuming that it is
sized.
This has to be sized because of some Rust limitations, as you
cant really pass an unsized argument to a function (for now). If you're looking to store unsized types (dyn Trait, [Type], etc) on a [RwData], see [RwData::new_unsized`].
source§impl<T> RwData<T>where
T: ?Sized + 'static,
impl<T> RwData<T>where
T: ?Sized + 'static,
sourcepub fn new_unsized<SizedT: 'static>(data: Arc<RwLock<T>>) -> Self
pub fn new_unsized<SizedT: 'static>(data: Arc<RwLock<T>>) -> Self
Returns a new instance of RwData<T>, assuming that it is
unsized.
This method is only required if you’re dealing with types that
may not be Sized (dyn Trait, [Type], etc). If the type
in question is sized, use RwData::new instead.
sourcepub fn read(&self) -> RwLockReadGuard<'_, T>
pub fn read(&self) -> RwLockReadGuard<'_, T>
Blocking reference to the information.
Also makes it so that has_changed returns false.
§Examples
Since this is a blocking read, the thread will hault while the data is being written to:
let read_write_data = RwData::new("☹️");
let read_only_data = RoData::from(&read_write_data);
let instant = Instant::now();
thread::scope(|scope| {
scope.spawn(|| {
let mut read_write = read_write_data.write();
// Supposedly long computations.
thread::sleep(Duration::from_millis(100));
*read_write = "☺️";
});
// Just making sure that the read happens slighly after the write.
thread::sleep(Duration::from_millis(1));
let read_only = read_only_data.read();
let time_elapsed = Instant::now().duration_since(instant);
assert!(time_elapsed >= Duration::from_millis(100));
assert!(*read_only == "☺️");
});Note that other reads will NOT block reading in this way, only writes:
let read_write_data = RwData::new("☹️");
let read_only_data = RoData::from(&read_write_data);
let instant = Instant::now();
thread::scope(|scope| {
scope.spawn(|| {
let read_only = read_write_data.read();
// The thread hangs, but reading is still possible.
thread::sleep(Duration::from_millis(100));
});
// Just making sure that this read happens slighly after the last one.
thread::sleep(Duration::from_millis(1));
let read_only = read_only_data.read();
let time_elapsed = Instant::now().duration_since(instant);
assert!(time_elapsed < Duration::from_millis(100));
});sourcepub fn inspect<U>(&self, f: impl FnOnce(&T) -> U) -> U
pub fn inspect<U>(&self, f: impl FnOnce(&T) -> U) -> U
Blocking inspection of the inner data.
Also makes it so that has_changed returns false.
§Examples
This method is useful if you want to scope the reading, or need to drop the reference quickly, so it can be written to.
You can do this:
let count = RwData::new(31);
let count_reader = RoData::from(&count);
// The read write counterpart to `inspect`.
count.mutate(|count| {
*count += 5;
add_to_count(count)
});
count_reader.inspect(|count| { /* reading operations */ });
*count.write() = new_count();Instead of this:
let count = RwData::new(31);
let count_reader = RoData::from(&count);
// The read write counterpart to `inspect`.
let mut count_write = count.write();
*count_write += 5;
add_to_count(&mut *count_write);
drop(count_write);
let count_read = count_reader.read();
// reading operations
drop(count_read);
*count.write() = new_count();Or this:
let count = RwData::new(31);
let count_reader = RoData::from(&count);
// The read write counterpart to `inspect`.
{
let mut count = count.write();
*count += 5;
add_to_count(&mut count)
}
{
let count = count.read();
// reading operations
}
*count.write() = new_count();sourcepub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>>
pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>>
Non blocking reference to the information.
If successful, also makes it so that has_changed returns
false.
§Examples
Unlike read, can fail to return a reference to the
underlying data:
let new_data = RwData::new("hello 👋");
let mut blocking_write = new_data.write();
*blocking_write = "bye 👋";
let try_read = new_data.try_read();
assert!(matches!(try_read, Err(TryLockError::WouldBlock)));sourcepub fn try_inspect<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>
pub fn try_inspect<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>
Non blocking inspection of the inner data.
If successful, also makes it so that has_changed returns
false.
§Examples
Unlike inspect, can fail to return a reference to the
underlying data:
let new_data = RwData::new("hello 👋");
let try_inspect = new_data.mutate(|blocking_mutate| {
*blocking_mutate = "bye 👋";
new_data.try_inspect(|try_inspect| *try_inspect == "bye 👋")
});
assert!(matches!(try_inspect, Err(TryLockError::WouldBlock)));sourcepub fn has_changed(&self) -> bool
pub fn has_changed(&self) -> bool
Wether or not it has changed since it was last read.
A “change” is defined as any time the methods write,
mutate, try_write, or try_mutate, are called on an
RwData. Once has_changed is called, the data will be
considered unchanged since the last has_changed call, for
that specific instance of a [ReadableData].
When first creating a [ReadableData] type, has_changed
will return false;
§Examples
use duat_core::data::{ReadableData, RoData, RwData};
let new_data = RwData::new("Initial text");
assert!(!new_data.has_changed());
let first_reader = RoData::from(&new_data);
*new_data.write() = "Final text";
let second_reader = RoData::from(&new_data);
assert!(first_reader.has_changed());
assert!(!second_reader.has_changed());sourcepub fn ptr_eq<U>(&self, other: &(impl Data<U> + ?Sized)) -> boolwhere
U: ?Sized,
pub fn ptr_eq<U>(&self, other: &(impl Data<U> + ?Sized)) -> boolwhere
U: ?Sized,
Returns true if both [ReadableData<T>]s point to the same
data.
§Examples
let data_1 = RwData::new(false);
let data_1_clone = data_1.clone();
let data_2 = RwData::new(true);
assert!(data_1.ptr_eq(&data_1_clone));
assert!(!data_1.ptr_eq(&data_2));sourcepub fn write(&self) -> ReadWriteGuard<'_, T>
pub fn write(&self) -> ReadWriteGuard<'_, T>
Blocking mutable reference to the information.
Also makes it so that has_changed returns true for
[self] or any of its clones, be they RoData or
RwData.
§Safety
Since this is a blocking function, you should be careful about the prevention of deadlocks, one of the few unsafe aspects of code that Rust doesn’t prevent.
As an example, this code will deadlock indefinitely:
let data_1 = RwData::new('😟');
let data_2 = RwData::new('😭');
thread::scope(|scope| {
scope.spawn(|| {
let mut data_1 = data_1.write();
thread::sleep(Duration::from_millis(100));
let mut data_2 = data_2.write();
mem::swap(&mut data_1, &mut data_2);
});
scope.spawn(|| {
let mut data_2 = data_2.write();
thread::sleep(Duration::from_millis(100));
let mut data_1 = data_1.write();
mem::swap(&mut data_1, &mut data_2);
});
});In general, try not to juggle multiple &mut handles to
RwDatas. A good way of doing that is the
RwData::mutate method, which makes it very explicit when a
specific handle will be dropped, mitigating the possibility of
deadlocks.
sourcepub fn try_write(&self) -> Option<ReadWriteGuard<'_, T>>
pub fn try_write(&self) -> Option<ReadWriteGuard<'_, T>>
Non Blocking mutable reference to the information.
Also makes it so that has_changed returns true for
[self] or any of its clones, be they RoData or
RwData.
§Safety
Unlike RwData::write, this method cannot cause deadlocks,
returning an Err instead.
let data_1 = RwData::new('😀');
let data_2 = RwData::new('😁');
thread::scope(|scope| {
scope.spawn(|| {
let mut data_1 = data_1.try_write();
thread::sleep(Duration::from_millis(100));
let mut data_2 = data_2.try_write();
if let (Ok(mut data_1), Ok(mut data_2)) = (data_1, data_2) {
mem::swap(&mut data_1, &mut data_2);
}
});
scope.spawn(|| {
let mut data_2 = data_2.try_write();
thread::sleep(Duration::from_millis(100));
let mut data_1 = data_1.try_write();
if let (Ok(mut data_1), Ok(mut data_2)) = (data_1, data_2) {
mem::swap(&mut data_1, &mut data_2);
}
});
});
// Two swaps will happen.
assert_eq!(*data_1.read(), '😀');
assert_eq!(*data_2.read(), '😁');The downside is that you may not want it to fail ever, in
which case, you should probably use RwData::write.
sourcepub fn mutate<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
pub fn mutate<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
Blocking mutation of the inner data.
Also makes it so that has_changed returns true for
[self] or any of its clones, be they RoData or
RwData.
§Safety
This method “technically” has the same problems as
RwData::write, where you can deadlock by calling it on
multiple instances of RwData, but it makes this much more
explicit:
let data_1 = RwData::new('😟');
let data_2 = RwData::new('😭');
thread::scope(|scope| {
scope.spawn(|| {
data_1.mutate(|data_1| {
thread::sleep(Duration::from_millis(100));
let mut data_2 = data_2.write();
mem::swap(data_1, &mut *data_2);
});
});
scope.spawn(|| {
data_2.mutate(|data_2| {
thread::sleep(Duration::from_millis(100));
let mut data_1 = data_1.write();
mem::swap(&mut *data_1, data_2);
});
});
});Generally, you should favor this method for longer functions in which the data is only needed for a short time.
sourcepub fn try_mutate<R>(&self, f: impl FnOnce(&mut T) -> R) -> Option<R>
pub fn try_mutate<R>(&self, f: impl FnOnce(&mut T) -> R) -> Option<R>
Non blocking mutation of the inner data.
Also makes it so that has_changed returns true for
[self] or any of its clones, be they RoData or
RwData.
§Safety
Much like RwData::try_write, this also can’t deadlock,
failing instead. Generally, you should use this method only
when you are fine with not writing the data.
source§impl<T> RwData<T>where
T: ?Sized + 'static,
impl<T> RwData<T>where
T: ?Sized + 'static,
sourcepub fn data_is<U>(&self) -> boolwhere
U: 'static,
pub fn data_is<U>(&self) -> boolwhere
U: 'static,
Returns true if the data is of the concrete type T.
§Examples
You may want this method if you’re storing a list of
RwData<dyn Trait>, and want to know, at runtime, what type
each element is:
let list: [RwData<dyn AsAny>; 3] = [
RwData::new_unsized(Arc::new(RwLock::new(DownCastableString(
String::from("I can show you the world"),
)))),
RwData::new_unsized(Arc::new(RwLock::new(DownCastableString(
String::from("Shining, shimmering, splendid"),
)))),
RwData::new_unsized(Arc::new(RwLock::new(DownCastableChar('🧞')))),
];
assert!(list[0].data_is::<DownCastableString>());
assert!(list[1].data_is::<DownCastableString>());
assert!(list[2].data_is::<DownCastableChar>());sourcepub fn try_downcast<U>(&self) -> Option<RwData<U>>where
U: 'static,
pub fn try_downcast<U>(&self) -> Option<RwData<U>>where
U: 'static,
Tries to downcast to a concrete type.
§Examples
You may want this method if you’re storing a list of
RwData<dyn Trait>, and want to know, at runtime, what type
each element is:
let list: [RwData<dyn AsAny>; 3] = [
RwData::new_unsized(Arc::new(RwLock::new(DownCastableString(
String::from("I can show you the world"),
)))),
RwData::new_unsized(Arc::new(RwLock::new(DownCastableString(
String::from("Shining, shimmering, splendid"),
)))),
RwData::new_unsized(Arc::new(RwLock::new(DownCastableChar('🧞')))),
];
let maybe_char = list[2].clone().try_downcast::<DownCastableChar>();
assert!(maybe_char.is_ok());
*maybe_char.unwrap().write() = DownCastableChar('👳');
let maybe_string = list[0].clone().try_downcast::<DownCastableChar>();
assert!(maybe_string.is_err());If you don’t need to write to the data, consider using
RwData::inspect_as. If you only need to know if the type
matches, consider using RwData::data_is.
sourcepub fn inspect_as<U: 'static, R>(&self, f: impl FnOnce(&U) -> R) -> Option<R>
pub fn inspect_as<U: 'static, R>(&self, f: impl FnOnce(&U) -> R) -> Option<R>
Blocking inspection of the inner data.
§Examples
You may want this method if you’re storing a list of
RwData<dyn Trait>, and want to know, at runtime, what type
each element is:
let list: [RwData<dyn AsAny>; 3] = [
RwData::new_unsized(Arc::new(RwLock::new(DownCastableString(
String::from("I can show you the world"),
)))),
RwData::new_unsized(Arc::new(RwLock::new(DownCastableString(
String::from("Shining, shimmering, splendid"),
)))),
RwData::new_unsized(Arc::new(RwLock::new(DownCastableChar('🧞')))),
];
assert!(matches!(
list[2].inspect_as::<DownCastableChar, char>(|char| char.0),
Some('🧞')
));
assert!(matches!(
list[1].inspect_as::<DownCastableChar, char>(|char| char.0),
None
));pub fn mutate_as<U: 'static, R>(&self, f: impl FnOnce(&mut U) -> R) -> Option<R>
source§impl<U> RwData<dyn ActiveWidget<U>>where
U: Ui,
impl<U> RwData<dyn ActiveWidget<U>>where
U: Ui,
pub fn to_passive(self) -> RwData<dyn PassiveWidget<U>>
Trait Implementations§
impl<T: ?Sized + Send> Send for RwData<T>
impl<T: ?Sized + Sync> Sync for RwData<T>
Auto Trait Implementations§
impl<T> !Freeze for RwData<T>
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§default unsafe fn clone_to_uninit(&self, dst: *mut T)
default unsafe fn clone_to_uninit(&self, dst: *mut T)
clone_to_uninit)