diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc5c91ac..9a1404ba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,15 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he - **macros**: Added the `part_reader!` macro to generate a partial reader from a reference of a reader. (#688 @M-Adoo) - **macros**: The `simple_declare` now supports the `stateless` meta attribute, `#[simple_declare(stateless)]`. (#688 @M-Adoo) +### Fixed + +- **Core**: `PartData` allows the use of a reference to create a write reference, which is unsafe. Introduce `PartRef` and `PartMut` to replace it. (#pr @M-Adoo) + + +### Breading + +- **core**: Removed `PartData`. (#pr @M-Adoo) + ## [0.4.0-alpha.22] - 2025-01-08 ### Fixed diff --git a/core/src/builtin_widgets/painting_style.rs b/core/src/builtin_widgets/painting_style.rs index c0d411514..9d4858b29 100644 --- a/core/src/builtin_widgets/painting_style.rs +++ b/core/src/builtin_widgets/painting_style.rs @@ -26,7 +26,7 @@ impl PaintingStyleWidget { match this.try_into_value() { Ok(this) => Provider::new(this.painting_style), Err(this) => Provider::value_of_writer( - this.map_writer(|w| PartData::from_ref_mut(&mut w.painting_style)), + this.map_writer(|w| PartMut::new(&mut w.painting_style)), Some(DirtyPhase::LayoutSubtree), ), } diff --git a/core/src/builtin_widgets/text_style.rs b/core/src/builtin_widgets/text_style.rs index f6b928eaa..a15d44ad9 100644 --- a/core/src/builtin_widgets/text_style.rs +++ b/core/src/builtin_widgets/text_style.rs @@ -25,7 +25,7 @@ impl TextStyleWidget { match this.try_into_value() { Ok(this) => Provider::new(this.text_style), Err(this) => Provider::value_of_writer( - this.map_writer(|w| PartData::from_ref_mut(&mut w.text_style)), + this.map_writer(|w| PartMut::new(&mut w.text_style)), Some(DirtyPhase::LayoutSubtree), ), } diff --git a/core/src/builtin_widgets/theme.rs b/core/src/builtin_widgets/theme.rs index 701512ddb..66c9cc0b8 100644 --- a/core/src/builtin_widgets/theme.rs +++ b/core/src/builtin_widgets/theme.rs @@ -130,11 +130,11 @@ impl Theme { load_fonts(&this); let container_color = this.map_reader(|t| { // Safety Note: In this instance, a copied value of the palette is utilized, - // which is not the correct method of using `PartData`. However, in this case, + // which is not the correct method of using `PartRef`. However, in this case, // it is only a read-only value, and once added to the providers, neither the // state reader nor its read reference can be accessed by anyone. Therefore, it // is considered safe. - unsafe { PartData::from_ptr(ContainerColor(t.palette.secondary_container())) } + unsafe { PartRef::from_ptr(ContainerColor(t.palette.secondary_container())) } }); let providers = smallvec![ diff --git a/core/src/query.rs b/core/src/query.rs index a687815df..f6483a895 100644 --- a/core/src/query.rs +++ b/core/src/query.rs @@ -390,7 +390,7 @@ impl dyn QueryAny { #[cfg(test)] mod tests { use super::*; - use crate::{prelude::PartData, reset_test_env, state::State}; + use crate::{prelude::PartMut, reset_test_env, state::State}; #[test] fn query_ref() { @@ -430,7 +430,7 @@ mod tests { } let x = State::value(X { a: 0, _b: 1 }); - let y = x.split_writer(|x| PartData::from_ref_mut(&mut x.a)); + let y = x.split_writer(|x| PartMut::new(&mut x.a)); { let h = y.query(&QueryId::of::()).unwrap(); assert!(h.downcast_ref::().is_some()); diff --git a/core/src/state.rs b/core/src/state.rs index 076d36c93..8e4c4a37f 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -11,7 +11,7 @@ pub use prior_op::*; use ribir_algo::Sc; use rxrust::ops::box_it::{BoxOp, CloneableBoxOp}; pub use splitted_state::*; -pub use state_cell::{PartData, ReadRef}; +pub use state_cell::*; use state_cell::{StateCell, ValueMutRef}; pub use stateful::*; pub use watcher::*; @@ -42,7 +42,7 @@ pub trait StateReader: 'static { #[inline] fn map_reader(&self, map: F) -> MapReader where - F: Fn(&Self::Value) -> PartData + Clone, + F: Fn(&Self::Value) -> PartRef + Clone, { MapReader { origin: self.clone_reader(), part_map: map } } @@ -123,7 +123,7 @@ pub trait StateWriter: StateWatcher { #[inline] fn split_writer(&self, mut_map: W) -> SplittedWriter where - W: Fn(&mut Self::Value) -> PartData + Clone, + W: Fn(&mut Self::Value) -> PartMut + Clone, { SplittedWriter::new(self.clone_writer(), mut_map) } @@ -142,7 +142,7 @@ pub trait StateWriter: StateWatcher { #[inline] fn map_writer(&self, part_map: M) -> MapWriter where - M: Fn(&mut Self::Value) -> PartData + Clone, + M: Fn(&mut Self::Value) -> PartMut + Clone, { let origin = self.clone_writer(); MapWriter { origin, part_map } @@ -270,9 +270,9 @@ impl State { impl<'a, V: ?Sized> WriteRef<'a, V> { pub fn map(mut orig: WriteRef<'a, V>, part_map: M) -> WriteRef<'a, U> where - M: Fn(&mut V) -> PartData, + M: Fn(&mut V) -> PartMut, { - let inner = part_map(&mut orig.value); + let inner = part_map(&mut orig.value).inner; let borrow = orig.value.borrow.clone(); let value = ValueMutRef { inner, borrow }; @@ -295,17 +295,18 @@ impl<'a, V: ?Sized> WriteRef<'a, V> { /// let c = Stateful::new(vec![1, 2, 3]); /// let b1: WriteRef<'_, Vec> = c.write(); /// let b2: Result, _> = - /// WriteRef::filter_map(b1, |v| v.get(1).map(PartData::from_ref)); + /// WriteRef::filter_map(b1, |v| v.get_mut(1).map(PartMut::::new)); /// assert_eq!(*b2.unwrap(), 2); /// ``` pub fn filter_map( mut orig: WriteRef<'a, V>, part_map: M, ) -> Result, Self> where - M: Fn(&mut V) -> Option>, + M: Fn(&mut V) -> Option>, { match part_map(&mut orig.value) { Some(inner) => { + let inner = inner.inner; let borrow = orig.value.borrow.clone(); let value = ValueMutRef { inner, borrow }; let WriteRef { modify_scope, info, .. } = orig; @@ -320,10 +321,11 @@ impl<'a, V: ?Sized> WriteRef<'a, V> { mut orig: WriteRef<'a, V>, f: F, ) -> (WriteRef<'a, U1>, WriteRef<'a, U2>) where - F: FnOnce(&mut V) -> (PartData, PartData), + F: FnOnce(&mut V) -> (PartMut, PartMut), { let WriteRef { info, modify_scope, modified, .. } = orig; let (a, b) = f(&mut *orig.value); + let (a, b) = (a.inner, b.inner); let borrow = orig.value.borrow.clone(); let a = ValueMutRef { inner: a, borrow: borrow.clone() }; let b = ValueMutRef { inner: b, borrow }; @@ -445,7 +447,7 @@ mod tests { reset_test_env!(); let origin = State::value(Origin { a: 0, b: 0 }); - let map_state = origin.map_writer(|v| PartData::from_ref_mut(&mut v.b)); + let map_state = origin.map_writer(|v| PartMut::new(&mut v.b)); let track_origin = Sc::new(Cell::new(0)); let track_map = Sc::new(Cell::new(0)); @@ -481,7 +483,7 @@ mod tests { reset_test_env!(); let origin = State::value(Origin { a: 0, b: 0 }); - let split = origin.split_writer(|v| PartData::from_ref_mut(&mut v.b)); + let split = origin.split_writer(|v| PartMut::new(&mut v.b)); let track_origin = Sc::new(Cell::new(0)); let track_split = Sc::new(Cell::new(0)); @@ -536,11 +538,11 @@ mod tests { let _map_writer_compose_widget = fn_widget! { Stateful::new((C, 0)) - .map_writer(|v| PartData::from_ref_mut(&mut v.0)) + .map_writer(|v| PartMut::new(&mut v.0)) }; let _split_writer_compose_widget = fn_widget! { Stateful::new((C, 0)) - .split_writer(|v| PartData::from_ref_mut(&mut v.0)) + .split_writer(|v| PartMut::new(&mut v.0)) }; } @@ -586,24 +588,24 @@ mod tests { let _map_writer_with_child = fn_widget! { let w = Stateful::new((CC, 0)) - .map_writer(|v| PartData::from_ref_mut(&mut v.0)); + .map_writer(|v| PartMut::new(&mut v.0)); @$w { @{ Void } } }; let _map_writer_without_child = fn_widget! { Stateful::new((CC, 0)) - .map_writer(|v| PartData::from_ref_mut(&mut v.0)) + .map_writer(|v| PartMut::new(&mut v.0)) }; let _split_writer_with_child = fn_widget! { let w = Stateful::new((CC, 0)) - .split_writer(|v| PartData::from_ref_mut(&mut v.0)); + .split_writer(|v| PartMut::new(&mut v.0)); @$w { @{ Void } } }; let _split_writer_without_child = fn_widget! { Stateful::new((CC, 0)) - .split_writer(|v| PartData::from_ref_mut(&mut v.0)) + .split_writer(|v| PartMut::new(&mut v.0)) }; } @@ -625,17 +627,17 @@ mod tests { }; let _map_reader_render_widget = fn_widget! { - Stateful::new((Void, 0)).map_reader(|v| PartData::from_ref(&v.0)) + Stateful::new((Void, 0)).map_reader(|v| PartRef::new(&v.0)) }; let _map_writer_render_widget = fn_widget! { Stateful::new((Void, 0)) - .map_writer(|v| PartData::from_ref_mut(&mut v.0)) + .map_writer(|v| PartMut::new(&mut v.0)) }; let _split_writer_render_widget = fn_widget! { Stateful::new((Void, 0)) - .split_writer(|v| PartData::from_ref_mut(&mut v.0)) + .split_writer(|v| PartMut::new(&mut v.0)) }; } @@ -644,11 +646,11 @@ mod tests { fn trait_object_part_data() { reset_test_env!(); let s = State::value(0); - let m = s.split_writer(|v| PartData::from_ref(v as &mut dyn Any)); + let m = s.split_writer(|v| PartMut::new(v as &mut dyn Any)); let v: ReadRef = m.read(); assert_eq!(*v.downcast_ref::().unwrap(), 0); - let s = s.map_writer(|v| PartData::from_ref(v as &mut dyn Any)); + let s = s.map_writer(|v| PartMut::new(v as &mut dyn Any)); let v: ReadRef = s.read(); assert_eq!(*v.downcast_ref::().unwrap(), 0); } diff --git a/core/src/state/map_state.rs b/core/src/state/map_state.rs index c18ed7c3f..6e11709d5 100644 --- a/core/src/state/map_state.rs +++ b/core/src/state/map_state.rs @@ -22,7 +22,7 @@ impl StateReader for MapReader where Self: 'static, S: StateReader, - M: Fn(&S::Value) -> PartData + Clone, + M: Fn(&S::Value) -> PartRef + Clone, { type Value = V; type OriginReader = S; @@ -52,7 +52,7 @@ impl StateReader for MapWriterAsReader where Self: 'static, S: StateReader, - M: Fn(&mut S::Value) -> PartData + Clone, + M: Fn(&mut S::Value) -> PartMut + Clone, { type Value = V; type OriginReader = S; @@ -84,7 +84,7 @@ impl StateReader for MapWriter where Self: 'static, S: StateWriter, - M: Fn(&mut S::Value) -> PartData + Clone, + M: Fn(&mut S::Value) -> PartMut + Clone, { type Value = V; type OriginReader = S; @@ -116,7 +116,7 @@ impl StateWatcher for MapWriter where Self: 'static, W: StateWriter, - M: Fn(&mut W::Value) -> PartData + Clone, + M: Fn(&mut W::Value) -> PartMut + Clone, { #[inline] fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible> { @@ -128,7 +128,7 @@ impl StateWriter for MapWriter where Self: 'static, W: StateWriter, - M: Fn(&mut W::Value) -> PartData + Clone, + M: Fn(&mut W::Value) -> PartMut + Clone, { type Writer = MapWriter; type OriginWriter = W; @@ -165,7 +165,7 @@ impl RenderProxy for MapReader where Self: 'static, S: StateReader, - F: Fn(&S::Value) -> PartData + Clone, + F: Fn(&S::Value) -> PartRef + Clone, V: Render, { #[inline] @@ -176,7 +176,7 @@ impl RenderProxy for MapWriterAsReader where Self: 'static, S: StateReader, - F: Fn(&mut S::Value) -> PartData + Clone, + F: Fn(&mut S::Value) -> PartMut + Clone, V: Render, { fn proxy(&self) -> impl Deref { self.read() } diff --git a/core/src/state/splitted_state.rs b/core/src/state/splitted_state.rs index 09d22dd9b..e4738401e 100644 --- a/core/src/state/splitted_state.rs +++ b/core/src/state/splitted_state.rs @@ -16,7 +16,7 @@ impl StateReader for SplittedWriter where Self: 'static, O: StateWriter, - W: Fn(&mut O::Value) -> PartData + Clone, + W: Fn(&mut O::Value) -> PartMut + Clone, { type Value = V; type OriginReader = O; @@ -48,7 +48,7 @@ impl StateWatcher for SplittedWriter where Self: 'static, O: StateWriter, - W: Fn(&mut O::Value) -> PartData + Clone, + W: Fn(&mut O::Value) -> PartMut + Clone, { fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, std::convert::Infallible> { self.info.notifier.raw_modifies().box_it() @@ -59,7 +59,7 @@ impl StateWriter for SplittedWriter where Self: 'static, O: StateWriter, - W: Fn(&mut O::Value) -> PartData + Clone, + W: Fn(&mut O::Value) -> PartMut + Clone, { type Writer = SplittedWriter; type OriginWriter = O; @@ -93,7 +93,7 @@ where impl SplittedWriter where O: StateWriter, - W: Fn(&mut O::Value) -> PartData + Clone, + W: Fn(&mut O::Value) -> PartMut + Clone, { pub(super) fn new(origin: O, mut_map: W) -> Self { Self { origin, splitter: mut_map, info: Sc::new(WriterInfo::new()) } @@ -108,8 +108,10 @@ where assert!(!orig.modified); orig.modify_scope.remove(ModifyScope::FRAMEWORK); orig.modified = true; - let value = - ValueMutRef { inner: (self.splitter)(&mut orig.value), borrow: orig.value.borrow.clone() }; + let value = ValueMutRef { + inner: (self.splitter)(&mut orig.value).inner, + borrow: orig.value.borrow.clone(), + }; WriteRef { value, modified: false, modify_scope, info: &self.info } } diff --git a/core/src/state/state_cell.rs b/core/src/state/state_cell.rs index 9430d29df..5c667d1fa 100644 --- a/core/src/state/state_cell.rs +++ b/core/src/state/state_cell.rs @@ -2,6 +2,7 @@ //! manage the borrow flag. use std::{ cell::{Cell, UnsafeCell}, + marker::PhantomData, ops::{Deref, DerefMut}, ptr::NonNull, }; @@ -57,7 +58,7 @@ impl StateCell { // SAFETY: `BorrowRef` ensures that there is only immutable access // to the value while borrowed. - let inner = PartData::PartRef(unsafe { NonNull::new_unchecked(self.data.get()) }); + let inner = InnerPart::Ref(unsafe { NonNull::new_unchecked(self.data.get()) }); ReadRef { inner, borrow: BorrowRef { borrow } } } @@ -84,7 +85,7 @@ impl StateCell { borrow.set(UNUSED - 1); let v_ref = BorrowRefMut { borrow }; - let inner = PartData::PartRef(unsafe { NonNull::new_unchecked(self.data.get()) }); + let inner = InnerPart::Ref(unsafe { NonNull::new_unchecked(self.data.get()) }); ValueMutRef { inner, borrow: v_ref } } @@ -93,28 +94,41 @@ impl StateCell { pub(super) fn into_inner(self) -> W { self.data.into_inner() } } -/// A partial data of a state, which should be point to the part data of the -/// state. -// todo: `PartData` should be a private type; otherwise, we can use a &T to create mutable data. +/// A partial reference value of a state, which should be point to the part data +/// of the state. #[derive(Clone)] -pub enum PartData { - PartRef(NonNull), - PartData(Box), +pub struct PartRef<'a, T: ?Sized> { + pub(crate) inner: InnerPart, + _phantom: PhantomData<&'a T>, } -impl PartData { - /// Create a `PartData` from a reference. - pub fn from_ref(v: &T) -> Self { PartData::PartRef(NonNull::from(v)) } +/// A partial mutable reference value of a state, which should be point to the +/// part data of the state. +#[derive(Clone)] +pub struct PartMut<'a, T: ?Sized> { + pub(crate) inner: InnerPart, + _phantom: PhantomData<&'a mut T>, +} + +#[derive(Clone)] +pub(crate) enum InnerPart { + Ref(NonNull), + // Box the `T` to allow it to be `?Sized`. + Ptr(Box), +} - /// Create a `PartData` from a mutable reference. - pub fn from_ref_mut(v: &mut T) -> Self { PartData::PartRef(NonNull::from(v)) } +impl<'a, T: ?Sized> PartRef<'a, T> { + /// Create a `PartRef` from a reference. + pub fn new(v: &T) -> Self { + Self { inner: InnerPart::Ref(NonNull::from(v)), _phantom: PhantomData } + } } -impl PartData { - /// Create a `PartData` from a pointer that points to the part data of the +impl<'a, T> PartRef<'a, T> { + /// Create a `PartRef` from a pointer that points to the part data of the /// original data. For example, `Option<&T>`, `Box`, `Arc`, `Rc`, etc. /// - /// The data used to create this `PartData` must point to the data in your + /// The data used to create this `PartRef` must point to the data in your /// original data. /// /// @@ -127,7 +141,7 @@ impl PartData { /// // We get the state of the second element. /// // `v.get(1)` returns an `Option<&i32>`, which is valid in the vector. /// let elem2 = vec.map_reader(|v| unsafe { - /// PartData::from_ptr(std::mem::transmute::<_, Option<&'static i32>>(v.get(1))) + /// PartRef::from_ptr(std::mem::transmute::<_, Option<&'static i32>>(v.get(1))) /// }); /// ``` /// @@ -140,14 +154,64 @@ impl PartData { /// /// let ab = Stateful::new((1, 2)); /// - /// let ab2 = ab.map_reader(|v| unsafe { PartData::from_ptr(*v) }); + /// let ab2 = ab.map_reader(|v| unsafe { PartRef::from_ptr(*v) }); + /// + /// // The `_a` may result in a dangling pointer issue since it utilizes the + /// // value of `ab2.read()`. However, `ab2` copies the value of `ab` rather + /// // than referencing it. When `ab2.read()` is dropped, `_a` still points to + /// // it, making access to `_a` dangerous. + /// let _a = ReadRef::map(ab2.read(), |v| unsafe { PartRef::from_ptr(v.0) }); + /// ``` + pub unsafe fn from_ptr(ptr_data: T) -> Self { + Self { inner: InnerPart::Ptr(Box::new(ptr_data)), _phantom: PhantomData } + } +} + +impl<'a, T: ?Sized> PartMut<'a, T> { + /// Create a `PartMut` from a mutable reference. + pub fn new(v: &mut T) -> Self { + Self { inner: InnerPart::Ref(NonNull::from(v)), _phantom: PhantomData } + } +} + +impl<'a, T> PartMut<'a, T> { + /// Create a `PartMut` from a pointer that points to the part data of the + /// original data. For example, `Option<&T>`, `Box`, `Arc`, `Rc`, etc. + /// + /// The data used to create this `PartMut` must point to the data in your + /// original data. + /// + /// + /// # Example + /// + /// ``` + /// use ribir_core::prelude::*; + /// + /// let vec = Stateful::new(vec![1, 2, 3]); + /// // We get the state of the second element. + /// // `v.get_mut(1)` returns an `Option<&mut i32>`, which is valid in the vector. + /// let elem2 = vec.map_writer(|v| unsafe { + /// PartMut::from_ptr(std::mem::transmute::<_, Option<&'static i32>>(v.get_mut(1))) + /// }); + /// ``` /// - /// // The `_a` may result in a dangling pointer issue since it utilizes the value of `ab2.read()`. However, `ab2` copies the - /// // value of `ab` rather than referencing it. - /// // When `ab2.read()` is dropped, `_a` still points to it, making access to `_a` dangerous. - /// let _a = ReadRef::map(ab2.read(), |v| unsafe { PartData::from_ptr(v.0) }); + /// # Safety + /// + /// Exercise caution when using this method, as it can lead to dangling + /// pointers in the state reference internals. /// ``` + /// use ribir_core::prelude::*; /// + /// let ab = Stateful::new((1, 2)); + /// + /// let ab2 = ab.map_writer(|v| unsafe { PartMut::from_ptr(*v) }); + /// + /// // The `_a` may result in a dangling pointer issue since it utilizes the + /// // value of `ab2.write()`. However, `ab2` copies the value of `ab` rather + /// // than referencing it. When `ab2.write()` is dropped, `_a` still points + /// // to it, making access to `_a` dangerous. + /// let _a = WriteRef::map(ab2.write(), |v| unsafe { PartMut::from_ptr(v.0) }); + /// ``` /// /// Otherwise, your modifications will not be applied to the state. /// ``` @@ -158,21 +222,23 @@ impl PartData { /// // We create a state of the second element. However, this state is a copy of /// // the vector because `v[1]` returns a copy of the value in the vector, not a /// // reference. - /// let mut elem2 = vec.map_writer(|v| unsafe { PartData::from_ptr(v[1]) }); + /// let mut elem2 = vec.map_writer(|v| unsafe { PartMut::from_ptr(v[1]) }); /// /// // This modification will not alter the `vec`. /// *elem2.write() = 20; /// ``` - pub unsafe fn from_ptr(ptr_data: T) -> Self { PartData::PartData(Box::new(ptr_data)) } + pub unsafe fn from_ptr(ptr_data: T) -> Self { + Self { inner: InnerPart::Ptr(Box::new(ptr_data)), _phantom: PhantomData } + } } pub struct ReadRef<'a, T: ?Sized> { - pub(crate) inner: PartData, + pub(crate) inner: InnerPart, pub(crate) borrow: BorrowRef<'a>, } pub(crate) struct ValueMutRef<'a, T: ?Sized> { - pub(crate) inner: PartData, + pub(crate) inner: InnerPart, pub(crate) borrow: BorrowRefMut<'a>, } @@ -184,21 +250,21 @@ pub(crate) struct BorrowRef<'b> { borrow: &'b Cell, } -impl Deref for PartData { +impl Deref for InnerPart { type Target = T; fn deref(&self) -> &Self::Target { match self { - PartData::PartRef(ptr) => unsafe { ptr.as_ref() }, - PartData::PartData(data) => data, + InnerPart::Ref(ptr) => unsafe { ptr.as_ref() }, + InnerPart::Ptr(data) => data, } } } -impl DerefMut for PartData { +impl DerefMut for InnerPart { fn deref_mut(&mut self) -> &mut Self::Target { match self { - PartData::PartRef(ptr) => unsafe { ptr.as_mut() }, - PartData::PartData(data) => data, + InnerPart::Ref(ptr) => unsafe { ptr.as_mut() }, + InnerPart::Ptr(data) => data, } } } @@ -255,8 +321,8 @@ impl BorrowRef<'_> { impl<'a, V: ?Sized> ReadRef<'a, V> { /// Make a new `ReadRef` by mapping the value of the current `ReadRef`. - pub fn map(r: ReadRef<'a, V>, f: impl FnOnce(&V) -> PartData) -> ReadRef<'a, U> { - ReadRef { inner: f(&r.inner), borrow: r.borrow } + pub fn map(r: ReadRef<'a, V>, f: impl FnOnce(&V) -> PartRef) -> ReadRef<'a, U> { + ReadRef { inner: f(&r.inner).inner, borrow: r.borrow } } /// Makes a new `ReadRef` for an optional component of the borrowed data. The @@ -274,18 +340,17 @@ impl<'a, V: ?Sized> ReadRef<'a, V> { /// /// let c = Stateful::new(vec![1, 2, 3]); /// let b1: ReadRef<'_, Vec> = c.read(); - /// let b2: Result, _> = - /// ReadRef::filter_map(b1, |v| v.get(1).map(PartData::from_ref)); + /// let b2: Result, _> = ReadRef::filter_map(b1, |v| v.get(1).map(PartRef::new)); /// assert_eq!(*b2.unwrap(), 2); /// ``` pub fn filter_map( orig: ReadRef<'a, V>, part_map: M, ) -> std::result::Result, Self> where - M: Fn(&V) -> Option>, + M: Fn(&V) -> Option>, { match part_map(&orig.inner) { - Some(inner) => Ok(ReadRef { inner, borrow: orig.borrow }), + Some(inner) => Ok(ReadRef { inner: inner.inner, borrow: orig.borrow }), None => Err(orig), } } @@ -293,29 +358,20 @@ impl<'a, V: ?Sized> ReadRef<'a, V> { /// Split the current `ReadRef` into two `ReadRef`s by mapping the value to /// two parts. pub fn map_split( - orig: ReadRef<'a, V>, f: impl FnOnce(&V) -> (PartData, PartData), + orig: ReadRef<'a, V>, f: impl FnOnce(&V) -> (PartRef, PartRef), ) -> (ReadRef<'a, U>, ReadRef<'a, W>) { let (a, b) = f(&*orig); let borrow = orig.borrow.clone(); - (ReadRef { inner: a, borrow: borrow.clone() }, ReadRef { inner: b, borrow }) + (ReadRef { inner: a.inner, borrow: borrow.clone() }, ReadRef { inner: b.inner, borrow }) } pub(crate) fn mut_as_ref_map( - orig: ReadRef<'a, V>, f: impl FnOnce(&mut V) -> PartData, + orig: ReadRef<'a, V>, f: impl FnOnce(&mut V) -> PartMut, ) -> ReadRef<'a, U> { - let ReadRef { inner: value, borrow } = orig; - let value = match value { - PartData::PartRef(mut ptr) => unsafe { - // Safety: This method is used to map a state to a part of it. Although a `&mut - // T` is passed to the closure, it is the user's responsibility to - // ensure that the closure does not modify the state. - f(ptr.as_mut()) - }, - PartData::PartData(mut data) => f(&mut data), - }; - - ReadRef { inner: value, borrow } + let ReadRef { mut inner, borrow } = orig; + let value = f(&mut inner); + ReadRef { inner: value.inner, borrow } } } @@ -351,3 +407,15 @@ impl Debug for WriteRef<'_, T> { impl Debug for QueryRef<'_, T> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { Debug::fmt(&**self, f) } } + +impl<'a, T: ?Sized> From> for PartRef<'a, T> { + fn from(part: PartMut) -> Self { Self { inner: part.inner, _phantom: PhantomData } } +} + +impl<'a, T> From<&'a T> for PartRef<'a, T> { + fn from(part: &'a T) -> Self { PartRef::new(part) } +} + +impl<'a, T> From<&'a mut T> for PartMut<'a, T> { + fn from(part: &'a mut T) -> Self { PartMut::new(part) } +} diff --git a/core/src/state/stateful.rs b/core/src/state/stateful.rs index 9e63e3fb6..734127e94 100644 --- a/core/src/state/stateful.rs +++ b/core/src/state/stateful.rs @@ -265,7 +265,7 @@ mod tests { { drop_writer_subscribe( #[allow(clippy::redundant_closure)] - Stateful::new(()).map_writer(|v| PartData::from_ref_mut(v)), + Stateful::new(()).map_writer(|v| PartMut::new(v)), drop_cnt.clone(), ); }; @@ -275,7 +275,7 @@ mod tests { { drop_writer_subscribe( #[allow(clippy::redundant_closure)] - Stateful::new(()).split_writer(|v| PartData::from_ref_mut(v)), + Stateful::new(()).split_writer(|v| PartMut::new(v)), drop_cnt.clone(), ); }; diff --git a/docs/en/get_started/quick_start.md b/docs/en/get_started/quick_start.md index 2925d7e43..d4097f05c 100644 --- a/docs/en/get_started/quick_start.md +++ b/docs/en/get_started/quick_start.md @@ -687,8 +687,8 @@ struct AppData { } let state = State::value(AppData { count: 0 }); -let map_count = state.map_writer(|d| PartData::from_ref_mut(&mut d.count)); -let split_count = state.split_writer(|d| PartData::from_ref_mut(&mut d.count)); +let map_count = state.map_writer(|d| PartMut::new(&mut d.count)); +let split_count = state.split_writer(|d| PartMut::new(&mut d.count)); watch!($state.count).subscribe(|_| println!("Parent data")); watch!(*$map_count).subscribe(|_| println!("Child(map) data")); @@ -759,7 +759,7 @@ struct AppData { } let state: State = State::value(AppData { count: 0 }); -let split_count = state.split_writer(|d| PartData::from_ref_mut(&mut d.count)); +let split_count = state.split_writer(|d| PartMut::new(&mut d.count)); // the root state's origin state is itself let _: &State = state.origin_reader(); diff --git a/docs/zh/get_started/quick_start.md b/docs/zh/get_started/quick_start.md index 90db6261d..1901b22f4 100644 --- a/docs/zh/get_started/quick_start.md +++ b/docs/zh/get_started/quick_start.md @@ -689,8 +689,8 @@ struct AppData { } let state = State::value(AppData { count: 0 }); -let map_count = state.map_writer(|d| PartData::from_ref(&mut d.count)); -let split_count = state.split_writer(|d| PartData::from_ref(&mut d.count)); +let map_count = state.map_writer(|d| PartMut::new(&mut d.count)); +let split_count = state.split_writer(|d| PartMut::new(&mut d.count)); watch!($state.count).subscribe(|_| println!("父状态数据")); watch!(*$map_count).subscribe(|_| println!("子状态(转换)数据")); @@ -762,7 +762,7 @@ struct AppData { } let state: State = State::value(AppData { count: 0 }); -let split_count = state.split_writer(|d| PartData::from_ref(&mut d.count)); +let split_count = state.split_writer(|d| PartMut::new(&mut d.count)); // 根状态的源状态是它自己 let _: &State = state.origin_reader(); diff --git a/examples/todos/src/ui.rs b/examples/todos/src/ui.rs index 02fc948aa..0fc1c699f 100644 --- a/examples/todos/src/ui.rs +++ b/examples/todos/src/ui.rs @@ -55,7 +55,7 @@ fn task_lists( let task = this.split_writer( // task will always exist, if the task is removed, // sthe widgets list will be rebuild. - move |todos| PartData::from_ref_mut(todos.get_task_mut(id).unwrap()), + move |todos| PartMut::new(todos.get_task_mut(id).unwrap()), ); let item = pipe!(*$editing == Some(id)) .value_chain(|s| s.distinct_until_changed().box_it()) @@ -141,10 +141,10 @@ where $item.write().opacity = 0.; let transform = item .get_transform_widget() - .map_writer(|w| PartData::from_ref_mut(&mut w.transform)); + .map_writer(|w| PartMut::new(&mut w.transform)); let opacity = item .get_opacity_widget() - .map_writer(|w| PartData::from_ref_mut(&mut w.opacity)); + .map_writer(|w| PartMut::new(&mut w.opacity)); let fly_in = stagger.push_state( (transform, opacity), (Transform::translation(0., 64.), 0.), diff --git a/macros/src/part_state.rs b/macros/src/part_state.rs index 90b0aa945..00a6861ef 100644 --- a/macros/src/part_state.rs +++ b/macros/src/part_state.rs @@ -20,7 +20,7 @@ pub fn gen_part_wrier(input: TokenStream, refs_ctx: &mut DollarRefsCtx) -> Token let PartState { and_token, mutability, state, dot, part_expr, tail_dot, tail_expr } = part; let tokens = quote_spanned! { state.span() => #host #dot map_writer( - |w| PartData::from_ref_mut(#and_token #mutability w #dot #part_expr #tail_dot #tail_expr) + |w| PartMut::new(#and_token #mutability w #dot #part_expr #tail_dot #tail_expr) ) }; refs_ctx.add_dollar_ref(info); @@ -38,7 +38,7 @@ pub fn gen_part_reader(input: TokenStream, refs_ctx: &mut DollarRefsCtx) -> Toke let PartState { and_token, mutability, state, dot, part_expr, tail_dot, tail_expr } = part; let tokens = quote_spanned! { state.span() => #host #dot map_reader( - |r| PartData::from_ref(#and_token #mutability r #dot #part_expr #tail_dot #tail_expr) + |r| PartRef::new(#and_token #mutability r #dot #part_expr #tail_dot #tail_expr) ) }; refs_ctx.add_dollar_ref(info); diff --git a/tests/rdl_macro_test.rs b/tests/rdl_macro_test.rs index 650f23351..e3fea0dee 100644 --- a/tests/rdl_macro_test.rs +++ b/tests/rdl_macro_test.rs @@ -827,10 +827,10 @@ fn fix_direct_use_map_writer_with_builtin() { fn _x(mut host: FatObj) { let _anchor = host .get_relative_anchor_widget() - .map_writer(|w| PartData::from_ref_mut(&mut w.anchor)); + .map_writer(|w| PartMut::new(&mut w.anchor)); let _anchor = host .get_relative_anchor_widget() - .map_writer(|w| PartData::from_ref_mut(&mut w.anchor)); + .map_writer(|w| PartMut::new(&mut w.anchor)); } } diff --git a/widgets/src/text_field.rs b/widgets/src/text_field.rs index 5a8059d23..a538f1ade 100644 --- a/widgets/src/text_field.rs +++ b/widgets/src/text_field.rs @@ -321,7 +321,7 @@ fn build_input_area( visible: pipe!(!$this.text.is_empty() || $theme.state == TextFieldState::Focused), }; input_area.get_visibility_widget() - .map_writer(|w| PartData::from_ref(&w.visible)) + .map_writer(|w| PartMut::new(&mut w.visible)) .transition(transitions::LINEAR.of(BuildCtx::get())); let mut input = @Input{ style: pipe!($theme.text.clone()) }; @@ -383,7 +383,7 @@ impl Compose for TextFieldLabel { text_style: pipe!($this.style.clone()), }; - this.map_writer(|w| PartData::from_ref(&w.style.font_size)) + this.map_writer(|w| PartMut::new(&mut w.style.font_size)) .transition(transitions::LINEAR.of(BuildCtx::get())); label @@ -402,9 +402,7 @@ fn build_content_area( padding: pipe!($theme.input_padding($this.text.is_empty())), }; - content_area - .get_padding_widget() - .map_writer(|w| PartData::from_ref(&w.padding)) + part_writer!(&mut content_area.padding) .transition(transitions::LINEAR.of(BuildCtx::get())); @ $content_area {