Merge pull request #2022 from ealmloff/signal-lifetimes

Restore signal lifetimes; rename take to manually drop
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-03-08 12:25:42 -08:00
commit 4a27b16cc5
19 files changed
+315 -166

No files matched your search

+1 -1
View File
@@ -80,7 +80,7 @@ fn BreedPic(breed: Signal<String>) -> Element {
.await .await
}); });
match fut.read().as_ref() { match fut.read_unchecked().as_ref() {
Some(Ok(resp)) => rsx! { Some(Ok(resp)) => rsx! {
button { onclick: move |_| fut.restart(), "Click to fetch another doggo" } button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
img { max_width: "500px", max_height: "500px", src: "{resp.message}" } img { max_width: "500px", max_height: "500px", src: "{resp.message}" }
+1 -1
View File
@@ -62,7 +62,7 @@ fn Doggo() -> Element {
.await .await
}); });
match fut.read().as_ref() { match fut.read_unchecked().as_ref() {
Some(Ok(resp)) => rsx! { Some(Ok(resp)) => rsx! {
button { onclick: move |_| fut.restart(), "Click to fetch another doggo" } button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
div { img { max_width: "500px", max_height: "500px", src: "{resp.message}" } } div { img { max_width: "500px", max_height: "500px", src: "{resp.message}" } }
+1 -2
View File
@@ -25,14 +25,13 @@ fn use_inner_html(id: &'static str) -> Option<String> {
.unwrap(); .unwrap();
if let Some(html) = res.as_str() { if let Some(html) = res.as_str() {
// serde_json::Value::String(html)
println!("html: {}", html); println!("html: {}", html);
value.set(Some(html.to_string())); value.set(Some(html.to_string()));
} }
}); });
}); });
value.read().clone() value()
} }
const EXPECTED_HTML: &str = r#"<div style="width: 100px; height: 100px; color: rgb(0, 0, 0);" id="5"><input type="checkbox"><h1>text</h1><div><p>hello world</p></div></div>"#; const EXPECTED_HTML: &str = r#"<div style="width: 100px; height: 100px; color: rgb(0, 0, 0);" id="5"><input type="checkbox"><h1>text</h1><div><p>hello world</p></div></div>"#;
+34 -20
View File
@@ -98,7 +98,7 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
/// Try to read the value. Returns None if the value is no longer valid. /// Try to read the value. Returns None if the value is no longer valid.
#[track_caller] #[track_caller]
pub fn try_read(&self) -> Result<S::Ref<T>, BorrowError> { pub fn try_read(&self) -> Result<S::Ref<'static, T>, BorrowError> {
if !self.validate() { if !self.validate() {
return Err(BorrowError::Dropped(ValueDroppedError { return Err(BorrowError::Dropped(ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_borrows"))] #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -129,13 +129,13 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
/// Read the value. Panics if the value is no longer valid. /// Read the value. Panics if the value is no longer valid.
#[track_caller] #[track_caller]
pub fn read(&self) -> S::Ref<T> { pub fn read(&self) -> S::Ref<'static, T> {
self.try_read().unwrap() self.try_read().unwrap()
} }
/// Try to write the value. Returns None if the value is no longer valid. /// Try to write the value. Returns None if the value is no longer valid.
#[track_caller] #[track_caller]
pub fn try_write(&self) -> Result<S::Mut<T>, BorrowMutError> { pub fn try_write(&self) -> Result<S::Mut<'static, T>, BorrowMutError> {
if !self.validate() { if !self.validate() {
return Err(BorrowMutError::Dropped(ValueDroppedError { return Err(BorrowMutError::Dropped(ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_borrows"))] #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -162,7 +162,7 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
/// Write the value. Panics if the value is no longer valid. /// Write the value. Panics if the value is no longer valid.
#[track_caller] #[track_caller]
pub fn write(&self) -> S::Mut<T> { pub fn write(&self) -> S::Mut<'static, T> {
self.try_write().unwrap() self.try_write().unwrap()
} }
@@ -186,8 +186,8 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
} }
} }
/// Take the value out of the generational box and invalidate the generational box. This will return the value if the value was taken. /// Drop the value out of the generational box and invalidate the generational box. This will return the value if the value was taken.
pub fn take(&self) -> Option<T> { pub fn manually_drop(&self) -> Option<T> {
if self.validate() { if self.validate() {
Storage::take(&self.raw.0.data) Storage::take(&self.raw.0.data)
} else { } else {
@@ -210,13 +210,13 @@ pub trait Storage<Data = ()>: AnyStorage + 'static {
fn try_read( fn try_read(
&'static self, &'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefBorrowInfo, #[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefBorrowInfo,
) -> Result<Self::Ref<Data>, BorrowError>; ) -> Result<Self::Ref<'static, Data>, BorrowError>;
/// Try to write the value. Returns None if the value is no longer valid. /// Try to write the value. Returns None if the value is no longer valid.
fn try_write( fn try_write(
&'static self, &'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefMutBorrowInfo, #[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefMutBorrowInfo,
) -> Result<Self::Mut<Data>, BorrowMutError>; ) -> Result<Self::Mut<'static, Data>, BorrowMutError>;
/// Set the value /// Set the value
fn set(&'static self, value: Data); fn set(&'static self, value: Data);
@@ -228,35 +228,49 @@ pub trait Storage<Data = ()>: AnyStorage + 'static {
/// A trait for any storage backing type. /// A trait for any storage backing type.
pub trait AnyStorage: Default { pub trait AnyStorage: Default {
/// The reference this storage type returns. /// The reference this storage type returns.
type Ref<T: ?Sized + 'static>: Deref<Target = T> + 'static; type Ref<'a, T: ?Sized + 'static>: Deref<Target = T>;
/// The mutable reference this storage type returns. /// The mutable reference this storage type returns.
type Mut<T: ?Sized + 'static>: DerefMut<Target = T> + 'static; type Mut<'a, T: ?Sized + 'static>: DerefMut<Target = T>;
/// Downcast a reference in a Ref to a more specific lifetime
///
/// This function enforces the variance of the lifetime parameter `'a` in Ref. Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
ref_: Self::Ref<'a, T>,
) -> Self::Ref<'b, T>;
/// Downcast a mutable reference in a RefMut to a more specific lifetime
///
/// This function enforces the variance of the lifetime parameter `'a` in Mut. Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
mut_: Self::Mut<'a, T>,
) -> Self::Mut<'b, T>;
/// Try to map the mutable ref. /// Try to map the mutable ref.
fn try_map_mut<T: ?Sized, U: ?Sized + 'static>( fn try_map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<T>, mut_ref: Self::Mut<'_, T>,
f: impl FnOnce(&mut T) -> Option<&mut U>, f: impl FnOnce(&mut T) -> Option<&mut U>,
) -> Option<Self::Mut<U>>; ) -> Option<Self::Mut<'_, U>>;
/// Map the mutable ref. /// Map the mutable ref.
fn map_mut<T: ?Sized, U: ?Sized + 'static>( fn map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<T>, mut_ref: Self::Mut<'_, T>,
f: impl FnOnce(&mut T) -> &mut U, f: impl FnOnce(&mut T) -> &mut U,
) -> Self::Mut<U> { ) -> Self::Mut<'_, U> {
Self::try_map_mut(mut_ref, |v| Some(f(v))).unwrap() Self::try_map_mut(mut_ref, |v| Some(f(v))).unwrap()
} }
/// Try to map the ref. /// Try to map the ref.
fn try_map<T: ?Sized, U: ?Sized + 'static>( fn try_map<T: ?Sized, U: ?Sized + 'static>(
ref_: Self::Ref<T>, ref_: Self::Ref<'_, T>,
f: impl FnOnce(&T) -> Option<&U>, f: impl FnOnce(&T) -> Option<&U>,
) -> Option<Self::Ref<U>>; ) -> Option<Self::Ref<'_, U>>;
/// Map the ref. /// Map the ref.
fn map<T: ?Sized, U: ?Sized + 'static>( fn map<T: ?Sized, U: ?Sized + 'static>(
ref_: Self::Ref<T>, ref_: Self::Ref<'_, T>,
f: impl FnOnce(&T) -> &U, f: impl FnOnce(&T) -> &U,
) -> Self::Ref<U> { ) -> Self::Ref<'_, U> {
Self::try_map(ref_, |v| Some(f(v))).unwrap() Self::try_map(ref_, |v| Some(f(v))).unwrap()
} }
+22 -10
View File
@@ -20,13 +20,25 @@ fn sync_runtime() -> &'static Arc<Mutex<Vec<MemoryLocation<SyncStorage>>>> {
} }
impl AnyStorage for SyncStorage { impl AnyStorage for SyncStorage {
type Ref<R: ?Sized + 'static> = GenerationalRef<MappedRwLockReadGuard<'static, R>>; type Ref<'a, R: ?Sized + 'static> = GenerationalRef<MappedRwLockReadGuard<'a, R>>;
type Mut<W: ?Sized + 'static> = GenerationalRefMut<MappedRwLockWriteGuard<'static, W>>; type Mut<'a, W: ?Sized + 'static> = GenerationalRefMut<MappedRwLockWriteGuard<'a, W>>;
fn try_map<I: ?Sized, U: ?Sized + 'static>( fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
ref_: Self::Ref<I>, ref_: Self::Ref<'a, T>,
) -> Self::Ref<'b, T> {
ref_
}
fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
mut_: Self::Mut<'a, T>,
) -> Self::Mut<'b, T> {
mut_
}
fn try_map<I: ?Sized + 'static, U: ?Sized + 'static>(
ref_: Self::Ref<'_, I>,
f: impl FnOnce(&I) -> Option<&U>, f: impl FnOnce(&I) -> Option<&U>,
) -> Option<Self::Ref<U>> { ) -> Option<Self::Ref<'_, U>> {
let GenerationalRef { let GenerationalRef {
inner, inner,
#[cfg(any(debug_assertions, feature = "debug_borrows"))] #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -46,10 +58,10 @@ impl AnyStorage for SyncStorage {
}) })
} }
fn try_map_mut<I: ?Sized, U: ?Sized + 'static>( fn try_map_mut<I: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<I>, mut_ref: Self::Mut<'_, I>,
f: impl FnOnce(&mut I) -> Option<&mut U>, f: impl FnOnce(&mut I) -> Option<&mut U>,
) -> Option<Self::Mut<U>> { ) -> Option<Self::Mut<'_, U>> {
let GenerationalRefMut { let GenerationalRefMut {
inner, inner,
#[cfg(any(debug_assertions, feature = "debug_borrows"))] #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -101,7 +113,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
&'static self, &'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefBorrowInfo, at: crate::GenerationalRefBorrowInfo,
) -> Result<Self::Ref<T>, error::BorrowError> { ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
let read = self.0.try_read(); let read = self.0.try_read();
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
@@ -132,7 +144,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
&'static self, &'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefMutBorrowInfo, at: crate::GenerationalRefMutBorrowInfo,
) -> Result<Self::Mut<T>, error::BorrowMutError> { ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
let write = self.0.try_write(); let write = self.0.try_write();
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+22 -10
View File
@@ -15,7 +15,7 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefBorrowInfo, at: crate::GenerationalRefBorrowInfo,
) -> Result<Self::Ref<T>, error::BorrowError> { ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
let borrow = self.0.try_borrow(); let borrow = self.0.try_borrow();
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
@@ -46,7 +46,7 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
&'static self, &'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefMutBorrowInfo, at: crate::GenerationalRefMutBorrowInfo,
) -> Result<Self::Mut<T>, error::BorrowMutError> { ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
let borrow = self.0.try_borrow_mut(); let borrow = self.0.try_borrow_mut();
#[cfg(any(debug_assertions, feature = "debug_ownership"))] #[cfg(any(debug_assertions, feature = "debug_ownership"))]
@@ -89,13 +89,25 @@ thread_local! {
} }
impl AnyStorage for UnsyncStorage { impl AnyStorage for UnsyncStorage {
type Ref<R: ?Sized + 'static> = GenerationalRef<Ref<'static, R>>; type Ref<'a, R: ?Sized + 'static> = GenerationalRef<Ref<'a, R>>;
type Mut<W: ?Sized + 'static> = GenerationalRefMut<RefMut<'static, W>>; type Mut<'a, W: ?Sized + 'static> = GenerationalRefMut<RefMut<'a, W>>;
fn try_map<I: ?Sized, U: ?Sized + 'static>( fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
_self: Self::Ref<I>, ref_: Self::Ref<'a, T>,
) -> Self::Ref<'b, T> {
ref_
}
fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
mut_: Self::Mut<'a, T>,
) -> Self::Mut<'b, T> {
mut_
}
fn try_map<I: ?Sized + 'static, U: ?Sized + 'static>(
_self: Self::Ref<'_, I>,
f: impl FnOnce(&I) -> Option<&U>, f: impl FnOnce(&I) -> Option<&U>,
) -> Option<Self::Ref<U>> { ) -> Option<Self::Ref<'_, U>> {
let GenerationalRef { let GenerationalRef {
inner, inner,
#[cfg(any(debug_assertions, feature = "debug_borrows"))] #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -109,10 +121,10 @@ impl AnyStorage for UnsyncStorage {
}) })
} }
fn try_map_mut<I: ?Sized, U: ?Sized + 'static>( fn try_map_mut<I: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<I>, mut_ref: Self::Mut<'_, I>,
f: impl FnOnce(&mut I) -> Option<&mut U>, f: impl FnOnce(&mut I) -> Option<&mut U>,
) -> Option<Self::Mut<U>> { ) -> Option<Self::Mut<'_, U>> {
let GenerationalRefMut { let GenerationalRefMut {
inner, inner,
#[cfg(any(debug_assertions, feature = "debug_borrows"))] #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+1 -1
View File
@@ -123,7 +123,7 @@ fn Route3(dynamic: String) -> Element {
oninput: move |evt| { oninput: move |evt| {
*current_route_str.write() = evt.value(); *current_route_str.write() = evt.value();
}, },
value: "{current_route_str.read()}" value: "{current_route_str}"
} }
"dynamic: {dynamic}" "dynamic: {dynamic}"
Link { to: Route::Route2 { user_id: 8888 }, "hello world link" } Link { to: Route::Route2 { user_id: 8888 }, "hello world link" }
+8 -8
View File
@@ -157,7 +157,7 @@ impl RouterContext {
/// Will fail silently if there is no previous location to go to. /// Will fail silently if there is no previous location to go to.
pub fn go_back(&self) { pub fn go_back(&self) {
{ {
self.inner.clone().write().history.go_back(); self.inner.write_unchecked().history.go_back();
} }
self.change_route(); self.change_route();
@@ -168,7 +168,7 @@ impl RouterContext {
/// Will fail silently if there is no next location to go to. /// Will fail silently if there is no next location to go to.
pub fn go_forward(&self) { pub fn go_forward(&self) {
{ {
self.inner.clone().write().history.go_forward(); self.inner.write_unchecked().history.go_forward();
} }
self.change_route(); self.change_route();
@@ -179,7 +179,7 @@ impl RouterContext {
target: NavigationTarget<Rc<dyn Any>>, target: NavigationTarget<Rc<dyn Any>>,
) -> Option<ExternalNavigationFailure> { ) -> Option<ExternalNavigationFailure> {
{ {
let mut write = self.inner.clone().write(); let mut write = self.inner.write_unchecked();
match target { match target {
NavigationTarget::Internal(p) => write.history.push(p), NavigationTarget::Internal(p) => write.history.push(p),
NavigationTarget::External(e) => return write.external(e), NavigationTarget::External(e) => return write.external(e),
@@ -195,7 +195,7 @@ impl RouterContext {
pub fn push(&self, target: impl Into<IntoRoutable>) -> Option<ExternalNavigationFailure> { pub fn push(&self, target: impl Into<IntoRoutable>) -> Option<ExternalNavigationFailure> {
let target = self.resolve_into_routable(target.into()); let target = self.resolve_into_routable(target.into());
{ {
let mut write = self.inner.clone().write(); let mut write = self.inner.write_unchecked();
match target { match target {
NavigationTarget::Internal(p) => write.history.push(p), NavigationTarget::Internal(p) => write.history.push(p),
NavigationTarget::External(e) => return write.external(e), NavigationTarget::External(e) => return write.external(e),
@@ -212,7 +212,7 @@ impl RouterContext {
let target = self.resolve_into_routable(target.into()); let target = self.resolve_into_routable(target.into());
{ {
let mut state = self.inner.clone().write(); let mut state = self.inner.write_unchecked();
match target { match target {
NavigationTarget::Internal(p) => state.history.replace(p), NavigationTarget::Internal(p) => state.history.replace(p),
NavigationTarget::External(e) => return state.external(e), NavigationTarget::External(e) => return state.external(e),
@@ -276,14 +276,14 @@ impl RouterContext {
/// Clear any unresolved errors /// Clear any unresolved errors
pub fn clear_error(&self) { pub fn clear_error(&self) {
let mut write_inner = self.inner.clone().write(); let mut write_inner = self.inner.write_unchecked();
write_inner.unresolved_error = None; write_inner.unresolved_error = None;
write_inner.update_subscribers(); write_inner.update_subscribers();
} }
pub(crate) fn render_error(&self) -> Element { pub(crate) fn render_error(&self) -> Element {
let inner_read = self.inner.clone().write(); let inner_read = self.inner.write_unchecked();
inner_read inner_read
.unresolved_error .unresolved_error
.as_ref() .as_ref()
@@ -297,7 +297,7 @@ impl RouterContext {
let callback = callback.clone(); let callback = callback.clone();
drop(self_read); drop(self_read);
if let Some(new) = callback(myself) { if let Some(new) = callback(myself) {
let mut self_write = self.inner.clone().write(); let mut self_write = self.inner.write_unchecked();
match new { match new {
NavigationTarget::Internal(p) => self_write.history.replace(p), NavigationTarget::Internal(p) => self_write.history.replace(p),
NavigationTarget::External(e) => return self_write.external(e), NavigationTarget::External(e) => return self_write.external(e),
+4 -4
View File
@@ -30,9 +30,9 @@ fn app() -> Element {
#[component] #[component]
fn Read() -> Element { fn Read() -> Element {
let mut signal = use_signal_sync(|| 0); let signal = use_signal_sync(|| 0);
let _write = signal.write(); let _write = signal.write_unchecked();
let _read = signal.read(); let _read = signal.read();
unreachable!() unreachable!()
@@ -40,10 +40,10 @@ fn Read() -> Element {
#[component] #[component]
fn ReadMut() -> Element { fn ReadMut() -> Element {
let mut signal = use_signal_sync(|| 0); let signal = use_signal_sync(|| 0);
let _read = signal.read(); let _read = signal.read();
let _write = signal.write(); let _write = signal.write_unchecked();
unreachable!() unreachable!()
} }
+21 -17
View File
@@ -15,6 +15,7 @@ use generational_box::{GenerationalBox, Owner, Storage};
use crate::ReadableRef; use crate::ReadableRef;
use crate::Writable; use crate::Writable;
use crate::WritableRef;
use crate::{ReactiveContext, Readable}; use crate::{ReactiveContext, Readable};
/// Run a closure with the given owner. /// Run a closure with the given owner.
@@ -189,11 +190,9 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
} }
} }
/// Take the value out of the CopyValue, invalidating the value in the process. /// Manually drop the value in the CopyValue, invalidating the value in the process.
pub fn take(&self) -> T { pub fn manually_drop(&self) -> Option<T> {
self.value self.value.manually_drop()
.take()
.expect("value is already dropped or borrowed")
} }
/// Get the scope this value was created in. /// Get the scope this value was created in.
@@ -211,40 +210,45 @@ impl<T: 'static, S: Storage<T>> Readable for CopyValue<T, S> {
type Target = T; type Target = T;
type Storage = S; type Storage = S;
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> { fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
self.value.try_read() self.value.try_read()
} }
fn peek(&self) -> ReadableRef<Self> { fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
self.value.read() self.value.read()
} }
} }
impl<T: 'static, S: Storage<T>> Writable for CopyValue<T, S> { impl<T: 'static, S: Storage<T>> Writable for CopyValue<T, S> {
type Mut<R: ?Sized + 'static> = S::Mut<R>; type Mut<'a, R: ?Sized + 'static> = S::Mut<'a, R>;
fn map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> &mut U>( fn map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> &mut U>(
mut_: Self::Mut<I>, mut_: Self::Mut<'_, I>,
f: F, f: F,
) -> Self::Mut<U> { ) -> Self::Mut<'_, U> {
S::map_mut(mut_, f) S::map_mut(mut_, f)
} }
fn try_map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> Option<&mut U>>( fn try_map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> Option<&mut U>>(
mut_: Self::Mut<I>, mut_: Self::Mut<'_, I>,
f: F, f: F,
) -> Option<Self::Mut<U>> { ) -> Option<Self::Mut<'_, U>> {
S::try_map_mut(mut_, f) S::try_map_mut(mut_, f)
} }
#[track_caller] fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
fn try_write(&mut self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> { mut_: Self::Mut<'a, R>,
self.value.try_write() ) -> Self::Mut<'b, R> {
S::downcast_lifetime_mut(mut_)
} }
#[track_caller] #[track_caller]
fn write(&mut self) -> Self::Mut<T> { fn try_write_unchecked(
self.value.write() &self,
) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
self.value.try_write()
} }
#[track_caller] #[track_caller]
+6 -4
View File
@@ -56,13 +56,15 @@ impl<T: PartialEq + 'static> Readable for GlobalMemo<T> {
type Storage = UnsyncStorage; type Storage = UnsyncStorage;
#[track_caller] #[track_caller]
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> { fn try_read_unchecked(
self.memo().try_read() &self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
self.memo().try_read_unchecked()
} }
#[track_caller] #[track_caller]
fn peek(&self) -> ReadableRef<Self> { fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
self.memo().peek() self.memo().peek_unchecked()
} }
} }
+24 -14
View File
@@ -1,6 +1,6 @@
use crate::write::Writable; use crate::write::Writable;
use crate::Write;
use crate::{read::Readable, ReadableRef}; use crate::{read::Readable, ReadableRef};
use crate::{WritableRef, Write};
use dioxus_core::prelude::{IntoAttributeValue, ScopeId}; use dioxus_core::prelude::{IntoAttributeValue, ScopeId};
use generational_box::UnsyncStorage; use generational_box::UnsyncStorage;
use std::{mem::MaybeUninit, ops::Deref}; use std::{mem::MaybeUninit, ops::Deref};
@@ -44,8 +44,8 @@ impl<T: 'static> GlobalSignal<T> {
} }
/// Write this value /// Write this value
pub fn write(&self) -> Write<T, UnsyncStorage> { pub fn write(&self) -> Write<'static, T, UnsyncStorage> {
self.signal().write() self.signal().try_write_unchecked().unwrap()
} }
/// Get the scope the signal was created in. /// Get the scope the signal was created in.
@@ -71,23 +71,25 @@ impl<T: 'static> Readable for GlobalSignal<T> {
type Storage = UnsyncStorage; type Storage = UnsyncStorage;
#[track_caller] #[track_caller]
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> { fn try_read_unchecked(
self.signal().try_read() &self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
self.signal().try_read_unchecked()
} }
#[track_caller] #[track_caller]
fn peek(&self) -> ReadableRef<Self> { fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
self.signal().peek() self.signal().peek_unchecked()
} }
} }
impl<T: 'static> Writable for GlobalSignal<T> { impl<T: 'static> Writable for GlobalSignal<T> {
type Mut<R: ?Sized + 'static> = Write<R, UnsyncStorage>; type Mut<'a, R: ?Sized + 'static> = Write<'a, R, UnsyncStorage>;
fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>( fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
ref_: Self::Mut<I>, ref_: Self::Mut<'_, I>,
f: F, f: F,
) -> Self::Mut<U> { ) -> Self::Mut<'_, U> {
Write::map(ref_, f) Write::map(ref_, f)
} }
@@ -96,15 +98,23 @@ impl<T: 'static> Writable for GlobalSignal<T> {
U: ?Sized + 'static, U: ?Sized + 'static,
F: FnOnce(&mut I) -> Option<&mut U>, F: FnOnce(&mut I) -> Option<&mut U>,
>( >(
ref_: Self::Mut<I>, ref_: Self::Mut<'_, I>,
f: F, f: F,
) -> Option<Self::Mut<U>> { ) -> Option<Self::Mut<'_, U>> {
Write::filter_map(ref_, f) Write::filter_map(ref_, f)
} }
fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
mut_: Self::Mut<'a, R>,
) -> Self::Mut<'b, R> {
Write::downcast_lifetime(mut_)
}
#[track_caller] #[track_caller]
fn try_write(&mut self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> { fn try_write_unchecked(
self.signal().try_write() &self,
) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
self.signal().try_write_unchecked()
} }
} }
+10 -6
View File
@@ -6,8 +6,8 @@ use generational_box::{AnyStorage, UnsyncStorage};
/// A read only signal that has been mapped to a new type. /// A read only signal that has been mapped to a new type.
pub struct MappedSignal<O: ?Sized + 'static, S: AnyStorage = UnsyncStorage> { pub struct MappedSignal<O: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
try_read: Rc<dyn Fn() -> Result<S::Ref<O>, generational_box::BorrowError> + 'static>, try_read: Rc<dyn Fn() -> Result<S::Ref<'static, O>, generational_box::BorrowError> + 'static>,
peek: Rc<dyn Fn() -> S::Ref<O> + 'static>, peek: Rc<dyn Fn() -> S::Ref<'static, O> + 'static>,
} }
impl<O: ?Sized, S: AnyStorage> Clone for MappedSignal<O, S> { impl<O: ?Sized, S: AnyStorage> Clone for MappedSignal<O, S> {
@@ -26,8 +26,10 @@ where
{ {
/// Create a new mapped signal. /// Create a new mapped signal.
pub(crate) fn new( pub(crate) fn new(
try_read: Rc<dyn Fn() -> Result<S::Ref<O>, generational_box::BorrowError> + 'static>, try_read: Rc<
peek: Rc<dyn Fn() -> S::Ref<O> + 'static>, dyn Fn() -> Result<S::Ref<'static, O>, generational_box::BorrowError> + 'static,
>,
peek: Rc<dyn Fn() -> S::Ref<'static, O> + 'static>,
) -> Self { ) -> Self {
MappedSignal { try_read, peek } MappedSignal { try_read, peek }
} }
@@ -41,11 +43,13 @@ where
type Target = O; type Target = O;
type Storage = S; type Storage = S;
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> { fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
(self.try_read)() (self.try_read)()
} }
fn peek(&self) -> ReadableRef<Self> { fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
(self.peek)() (self.peek)()
} }
} }
+7 -5
View File
@@ -163,8 +163,10 @@ where
type Storage = UnsyncStorage; type Storage = UnsyncStorage;
#[track_caller] #[track_caller]
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> { fn try_read_unchecked(
let read = self.inner.try_read(); &self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
let read = self.inner.try_read_unchecked();
match read { match read {
Ok(r) => { Ok(r) => {
let needs_update = self let needs_update = self
@@ -175,7 +177,7 @@ where
if needs_update { if needs_update {
drop(r); drop(r);
self.recompute(); self.recompute();
self.inner.try_read() self.inner.try_read_unchecked()
} else { } else {
Ok(r) Ok(r)
} }
@@ -188,8 +190,8 @@ where
/// ///
/// If the signal has been dropped, this will panic. /// If the signal has been dropped, this will panic.
#[track_caller] #[track_caller]
fn peek(&self) -> ReadableRef<Self> { fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
self.inner.peek() self.inner.peek_unchecked()
} }
} }
+1 -2
View File
@@ -128,8 +128,7 @@ impl ReactiveContext {
/// ///
/// Returns true if the context was marked as dirty, or false if the context has been dropped /// Returns true if the context was marked as dirty, or false if the context has been dropped
pub fn mark_dirty(&self) -> bool { pub fn mark_dirty(&self) -> bool {
let mut copy = self.inner; if let Ok(mut self_write) = self.inner.try_write_unchecked() {
if let Ok(mut self_write) = copy.try_write() {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
tracing::trace!( tracing::trace!(
+39 -9
View File
@@ -6,7 +6,8 @@ use crate::MappedSignal;
/// A reference to a value that can be read from. /// A reference to a value that can be read from.
#[allow(type_alias_bounds)] #[allow(type_alias_bounds)]
pub type ReadableRef<T: Readable, O = <T as Readable>::Target> = <T::Storage as AnyStorage>::Ref<O>; pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
<T::Storage as AnyStorage>::Ref<'a, O>;
/// A trait for states that can be read from like [`crate::Signal`], [`crate::GlobalSignal`], or [`crate::ReadOnlySignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Readable`] type. /// A trait for states that can be read from like [`crate::Signal`], [`crate::GlobalSignal`], or [`crate::ReadOnlySignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Readable`] type.
pub trait Readable { pub trait Readable {
@@ -27,15 +28,17 @@ pub trait Readable {
let mapping = mapping.clone(); let mapping = mapping.clone();
move || { move || {
self_ self_
.try_read() .try_read_unchecked()
.map(|ref_| <Self::Storage as AnyStorage>::map(ref_, |r| mapping(r))) .map(|ref_| <Self::Storage as AnyStorage>::map(ref_, |r| mapping(r)))
} }
}) })
as Rc< as Rc<
dyn Fn() -> Result<ReadableRef<Self, O>, generational_box::BorrowError> + 'static, dyn Fn() -> Result<ReadableRef<'static, Self, O>, generational_box::BorrowError>
+ 'static,
>; >;
let peek = Rc::new(move || <Self::Storage as AnyStorage>::map(self.peek(), |r| mapping(r))) let peek = Rc::new(move || {
as Rc<dyn Fn() -> ReadableRef<Self, O> + 'static>; <Self::Storage as AnyStorage>::map(self.peek_unchecked(), |r| mapping(r))
}) as Rc<dyn Fn() -> ReadableRef<'static, Self, O> + 'static>;
MappedSignal::new(try_read, peek) MappedSignal::new(try_read, peek)
} }
@@ -47,12 +50,39 @@ pub trait Readable {
self.try_read().unwrap() self.try_read().unwrap()
} }
/// Try to get the current value of the state. If this is a signal, this will subscribe the current scope to the signal. If the value has been dropped, this will panic. /// Try to get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
#[track_caller] #[track_caller]
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError>; fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
self.try_read_unchecked()
.map(Self::Storage::downcast_lifetime_ref)
}
/// Try to get a reference to the value without checking the lifetime.
///
/// NOTE: This method is completely safe because borrow checking is done at runtime.
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>;
/// Tet a reference to the value without checking the lifetime.
///
/// NOTE: This method is completely safe because borrow checking is done at runtime.
fn read_unchecked(&self) -> ReadableRef<'static, Self> {
self.try_read_unchecked().unwrap()
}
/// Get the current value of the signal without checking the lifetime. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
///
/// If the signal has been dropped, this will panic.
///
/// NOTE: This method is completely safe because borrow checking is done at runtime.
fn peek_unchecked(&self) -> ReadableRef<'static, Self>;
/// Get the current value of the state without subscribing to updates. If the value has been dropped, this will panic. /// Get the current value of the state without subscribing to updates. If the value has been dropped, this will panic.
fn peek(&self) -> ReadableRef<Self>; #[track_caller]
fn peek(&self) -> ReadableRef<Self> {
Self::Storage::downcast_lifetime_ref(self.peek_unchecked())
}
/// Clone the inner value and return it. If the value has been dropped, this will panic. /// Clone the inner value and return it. If the value has been dropped, this will panic.
#[track_caller] #[track_caller]
@@ -171,7 +201,7 @@ pub struct ReadableValueIterator<'a, R> {
} }
impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIterator<'a, R> { impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIterator<'a, R> {
type Item = ReadableRef<R, T>; type Item = ReadableRef<'a, R, T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let index = self.index; let index = self.index;
+9 -5
View File
@@ -50,7 +50,9 @@ impl<T: 'static, S: Storage<SignalData<T>>> ReadOnlySignal<T, S> {
#[doc(hidden)] #[doc(hidden)]
/// This should only be used by the `rsx!` macro. /// This should only be used by the `rsx!` macro.
pub fn __take(&self) -> T { pub fn __take(&self) -> T {
self.inner.take() self.inner
.manually_drop()
.expect("Signal has already been dropped")
} }
} }
@@ -59,16 +61,18 @@ impl<T, S: Storage<SignalData<T>>> Readable for ReadOnlySignal<T, S> {
type Storage = S; type Storage = S;
#[track_caller] #[track_caller]
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> { fn try_read_unchecked(
self.inner.try_read() &self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
self.inner.try_read_unchecked()
} }
/// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.** /// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
/// ///
/// If the signal has been dropped, this will panic. /// If the signal has been dropped, this will panic.
#[track_caller] #[track_caller]
fn peek(&self) -> S::Ref<T> { fn peek_unchecked(&self) -> S::Ref<'static, T> {
self.inner.peek() self.inner.peek_unchecked()
} }
} }
+46 -22
View File
@@ -1,8 +1,8 @@
use crate::Memo;
use crate::{ use crate::{
read::Readable, write::Writable, CopyValue, GlobalMemo, GlobalSignal, ReactiveContext, read::Readable, write::Writable, CopyValue, GlobalMemo, GlobalSignal, ReactiveContext,
ReadableRef, ReadableRef,
}; };
use crate::{Memo, WritableRef};
use dioxus_core::{prelude::IntoAttributeValue, ScopeId}; use dioxus_core::{prelude::IntoAttributeValue, ScopeId};
use generational_box::{AnyStorage, Storage, SyncStorage, UnsyncStorage}; use generational_box::{AnyStorage, Storage, SyncStorage, UnsyncStorage};
use std::{ use std::{
@@ -136,9 +136,9 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
} }
} }
/// Take the value out of the signal, invalidating the signal in the process. /// Drop the value out of the signal, invalidating the signal in the process.
pub fn take(&self) -> T { pub fn manually_drop(&self) -> Option<T> {
self.inner.take().value self.inner.manually_drop().map(|i| i.value)
} }
/// Get the scope the signal was created in. /// Get the scope the signal was created in.
@@ -168,8 +168,10 @@ impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
type Storage = S; type Storage = S;
#[track_caller] #[track_caller]
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> { fn try_read_unchecked(
let inner = self.inner.try_read()?; &self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
let inner = self.inner.try_read_unchecked()?;
if let Some(reactive_context) = ReactiveContext::current() { if let Some(reactive_context) = ReactiveContext::current() {
tracing::trace!("Subscribing to the reactive context {}", reactive_context); tracing::trace!("Subscribing to the reactive context {}", reactive_context);
@@ -182,19 +184,20 @@ impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
/// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.** /// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
/// ///
/// If the signal has been dropped, this will panic. /// If the signal has been dropped, this will panic.
fn peek(&self) -> ReadableRef<Self> { #[track_caller]
let inner = self.inner.read(); fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
let inner = self.inner.try_read_unchecked().unwrap();
S::map(inner, |v| &v.value) S::map(inner, |v| &v.value)
} }
} }
impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> { impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
type Mut<R: ?Sized + 'static> = Write<R, S>; type Mut<'a, R: ?Sized + 'static> = Write<'a, R, S>;
fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>( fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
ref_: Self::Mut<I>, ref_: Self::Mut<'_, I>,
f: F, f: F,
) -> Self::Mut<U> { ) -> Self::Mut<'_, U> {
Write::map(ref_, f) Write::map(ref_, f)
} }
@@ -203,15 +206,23 @@ impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
U: ?Sized + 'static, U: ?Sized + 'static,
F: FnOnce(&mut I) -> Option<&mut U>, F: FnOnce(&mut I) -> Option<&mut U>,
>( >(
ref_: Self::Mut<I>, ref_: Self::Mut<'_, I>,
f: F, f: F,
) -> Option<Self::Mut<U>> { ) -> Option<Self::Mut<'_, U>> {
Write::filter_map(ref_, f) Write::filter_map(ref_, f)
} }
fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
mut_: Self::Mut<'a, R>,
) -> Self::Mut<'b, R> {
Write::downcast_lifetime(mut_)
}
#[track_caller] #[track_caller]
fn try_write(&mut self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> { fn try_write_unchecked(
self.inner.try_write().map(|inner| { &self,
) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
self.inner.try_write_unchecked().map(|inner| {
let borrow = S::map_mut(inner, |v| &mut v.value); let borrow = S::map_mut(inner, |v| &mut v.value);
Write { Write {
write: borrow, write: borrow,
@@ -273,14 +284,14 @@ impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
/// ///
/// T is the current type of the write /// T is the current type of the write
/// S is the storage type of the signal /// S is the storage type of the signal
pub struct Write<T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> { pub struct Write<'a, T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
write: S::Mut<T>, write: S::Mut<'a, T>,
drop_signal: Box<dyn Any>, drop_signal: Box<dyn Any>,
} }
impl<T: ?Sized + 'static, S: AnyStorage> Write<T, S> { impl<'a, T: ?Sized + 'static, S: AnyStorage> Write<'a, T, S> {
/// Map the mutable reference to the signal's value to a new type. /// Map the mutable reference to the signal's value to a new type.
pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, S> { pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, S> {
let Self { let Self {
write, drop_signal, .. write, drop_signal, ..
} = myself; } = myself;
@@ -294,16 +305,29 @@ impl<T: ?Sized + 'static, S: AnyStorage> Write<T, S> {
pub fn filter_map<O: ?Sized>( pub fn filter_map<O: ?Sized>(
myself: Self, myself: Self,
f: impl FnOnce(&mut T) -> Option<&mut O>, f: impl FnOnce(&mut T) -> Option<&mut O>,
) -> Option<Write<O, S>> { ) -> Option<Write<'a, O, S>> {
let Self { let Self {
write, drop_signal, .. write, drop_signal, ..
} = myself; } = myself;
let write = S::try_map_mut(write, f); let write = S::try_map_mut(write, f);
write.map(|write| Write { write, drop_signal }) write.map(|write| Write { write, drop_signal })
} }
/// Downcast the lifetime of the mutable reference to the signal's value.
///
/// This function enforces the variance of the lifetime parameter `'a` in Mut. Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
pub fn downcast_lifetime<'b>(mut_: Self) -> Write<'b, T, S>
where
'a: 'b,
{
Write {
write: S::downcast_lifetime_mut(mut_.write),
drop_signal: mut_.drop_signal,
}
}
} }
impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<T, S> { impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<'_, T, S> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@@ -311,7 +335,7 @@ impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<T, S> {
} }
} }
impl<T: ?Sized, S: AnyStorage> DerefMut for Write<T, S> { impl<T: ?Sized, S: AnyStorage> DerefMut for Write<'_, T, S> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.write &mut self.write
} }
+58 -25
View File
@@ -1,33 +1,60 @@
use std::ops::DerefMut; use std::ops::{DerefMut, IndexMut};
use std::ops::IndexMut;
use crate::read::Readable; use crate::read::Readable;
/// A reference to a value that can be read from.
#[allow(type_alias_bounds)]
pub type WritableRef<'a, T: Writable, O = <T as Readable>::Target> = T::Mut<'a, O>;
/// A trait for states that can be read from like [`crate::Signal`], or [`crate::GlobalSignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Writable`] type. /// A trait for states that can be read from like [`crate::Signal`], or [`crate::GlobalSignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Writable`] type.
pub trait Writable: Readable { pub trait Writable: Readable {
/// The type of the reference. /// The type of the reference.
type Mut<R: ?Sized + 'static>: DerefMut<Target = R> + 'static; type Mut<'a, R: ?Sized + 'static>: DerefMut<Target = R>;
/// Map the reference to a new type. /// Map the reference to a new type.
fn map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> &mut U>( fn map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> &mut U>(
ref_: Self::Mut<I>, ref_: Self::Mut<'_, I>,
f: F, f: F,
) -> Self::Mut<U>; ) -> Self::Mut<'_, U>;
/// Try to map the reference to a new type. /// Try to map the reference to a new type.
fn try_map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> Option<&mut U>>( fn try_map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> Option<&mut U>>(
ref_: Self::Mut<I>, ref_: Self::Mut<'_, I>,
f: F, f: F,
) -> Option<Self::Mut<U>>; ) -> Option<Self::Mut<'_, U>>;
/// Downcast a mutable reference in a RefMut to a more specific lifetime
///
/// This function enforces the variance of the lifetime parameter `'a` in Ref.
fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
mut_: Self::Mut<'a, T>,
) -> Self::Mut<'b, T>;
/// Get a mutable reference to the value. If the value has been dropped, this will panic. /// Get a mutable reference to the value. If the value has been dropped, this will panic.
#[track_caller] #[track_caller]
fn write(&mut self) -> Self::Mut<Self::Target> { fn write(&mut self) -> WritableRef<'_, Self> {
self.try_write().unwrap() self.try_write().unwrap()
} }
/// Try to get a mutable reference to the value. If the value has been dropped, this will panic. /// Try to get a mutable reference to the value.
fn try_write(&mut self) -> Result<Self::Mut<Self::Target>, generational_box::BorrowMutError>; #[track_caller]
fn try_write(&mut self) -> Result<WritableRef<'_, Self>, generational_box::BorrowMutError> {
self.try_write_unchecked().map(Self::downcast_lifetime_mut)
}
/// Try to get a mutable reference to the value without checking the lifetime.
///
/// NOTE: This method is completely safe because borrow checking is done at runtime.
fn try_write_unchecked(
&self,
) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError>;
/// Tet a mutable reference to the value without checking the lifetime.
///
/// NOTE: This method is completely safe because borrow checking is done at runtime.
fn write_unchecked(&self) -> WritableRef<'static, Self> {
self.try_write_unchecked().unwrap()
}
/// Run a function with a mutable reference to the value. If the value has been dropped, this will panic. /// Run a function with a mutable reference to the value. If the value has been dropped, this will panic.
#[track_caller] #[track_caller]
@@ -55,7 +82,10 @@ pub trait Writable: Readable {
/// Index into the inner value and return a reference to the result. /// Index into the inner value and return a reference to the result.
#[track_caller] #[track_caller]
fn index_mut<I>(&mut self, index: I) -> Self::Mut<<Self::Target as std::ops::Index<I>>::Output> fn index_mut<I>(
&mut self,
index: I,
) -> WritableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
where where
Self::Target: std::ops::IndexMut<I>, Self::Target: std::ops::IndexMut<I>,
{ {
@@ -84,15 +114,14 @@ pub trait Writable: Readable {
/// An extension trait for Writable<Option<T>> that provides some convenience methods. /// An extension trait for Writable<Option<T>> that provides some convenience methods.
pub trait WritableOptionExt<T: 'static>: Writable<Target = Option<T>> { pub trait WritableOptionExt<T: 'static>: Writable<Target = Option<T>> {
/// Gets the value out of the Option, or inserts the given value if the Option is empty. /// Gets the value out of the Option, or inserts the given value if the Option is empty.
fn get_or_insert(&mut self, default: T) -> Self::Mut<T> { fn get_or_insert(&mut self, default: T) -> WritableRef<'_, Self, T> {
self.get_or_insert_with(|| default) self.get_or_insert_with(|| default)
} }
/// Gets the value out of the Option, or inserts the value returned by the given function if the Option is empty. /// Gets the value out of the Option, or inserts the value returned by the given function if the Option is empty.
fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> Self::Mut<T> { fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> WritableRef<'_, Self, T> {
let borrow = self.read(); let is_none = self.read().is_none();
if borrow.is_none() { if is_none {
drop(borrow);
self.with_mut(|v| *v = Some(default())); self.with_mut(|v| *v = Some(default()));
Self::map_mut(self.write(), |v| v.as_mut().unwrap()) Self::map_mut(self.write(), |v| v.as_mut().unwrap())
} else { } else {
@@ -102,7 +131,7 @@ pub trait WritableOptionExt<T: 'static>: Writable<Target = Option<T>> {
/// Attempts to write the inner value of the Option. /// Attempts to write the inner value of the Option.
#[track_caller] #[track_caller]
fn as_mut(&mut self) -> Option<Self::Mut<T>> { fn as_mut(&mut self) -> Option<WritableRef<'_, Self, T>> {
Self::try_map_mut(self.write(), |v: &mut Option<T>| v.as_mut()) Self::try_map_mut(self.write(), |v: &mut Option<T>| v.as_mut())
} }
} }
@@ -178,36 +207,40 @@ pub trait WritableVecExt<T: 'static>: Writable<Target = Vec<T>> {
/// Try to mutably get an element from the vector. /// Try to mutably get an element from the vector.
#[track_caller] #[track_caller]
fn get_mut(&mut self, index: usize) -> Option<Self::Mut<T>> { fn get_mut(&mut self, index: usize) -> Option<WritableRef<'_, Self, T>> {
Self::try_map_mut(self.write(), |v: &mut Vec<T>| v.get_mut(index)) Self::try_map_mut(self.write(), |v: &mut Vec<T>| v.get_mut(index))
} }
/// Gets an iterator over the values of the vector. /// Gets an iterator over the values of the vector.
#[track_caller] #[track_caller]
fn iter_mut(&self) -> WritableValueIterator<Self> fn iter_mut(&mut self) -> WritableValueIterator<'_, Self>
where where
Self: Sized + Clone, Self: Sized + Clone,
{ {
WritableValueIterator { WritableValueIterator {
index: 0, index: 0,
value: self.clone(), value: self,
} }
} }
} }
/// An iterator over the values of a `Writable<Vec<T>>`. /// An iterator over the values of a `Writable<Vec<T>>`.
pub struct WritableValueIterator<R> { pub struct WritableValueIterator<'a, R> {
index: usize, index: usize,
value: R, value: &'a mut R,
} }
impl<T: 'static, R: Writable<Target = Vec<T>>> Iterator for WritableValueIterator<R> { impl<'a, T: 'static, R: Writable<Target = Vec<T>>> Iterator for WritableValueIterator<'a, R> {
type Item = R::Mut<T>; type Item = WritableRef<'a, R, T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let index = self.index; let index = self.index;
self.index += 1; self.index += 1;
self.value.get_mut(index) R::try_map_mut(
self.value.try_write_unchecked().unwrap(),
|v: &mut Vec<T>| v.get_mut(index),
)
.map(R::downcast_lifetime_mut)
} }
} }