From 31489167f7cde672dae94f3eb30b67ab6b6739dc Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 23 Dec 2023 13:52:23 -0600 Subject: [PATCH] use map in the signal iterator --- packages/generational-box/src/lib.rs | 8 ++--- packages/signals/examples/iterator.rs | 4 +-- packages/signals/src/impls.rs | 47 ++++++++++++++++++------ packages/signals/src/map.rs | 51 ++++++++++++++++++++++----- 4 files changed, 86 insertions(+), 24 deletions(-) diff --git a/packages/generational-box/src/lib.rs b/packages/generational-box/src/lib.rs index f56904ede..836256d73 100644 --- a/packages/generational-box/src/lib.rs +++ b/packages/generational-box/src/lib.rs @@ -479,7 +479,7 @@ impl Display for AlreadyBorrowedError { impl std::error::Error for AlreadyBorrowedError {} /// A reference to a value in a generational box. -pub struct GenerationalRef { +pub struct GenerationalRef { inner: Ref<'static, T>, #[cfg(any(debug_assertions, feature = "debug_borrows"))] borrow: GenerationalRefBorrowInfo, @@ -487,7 +487,7 @@ pub struct GenerationalRef { impl GenerationalRef { /// Map one ref type to another. - pub fn map(orig: GenerationalRef, f: F) -> GenerationalRef + pub fn map(orig: GenerationalRef, f: F) -> GenerationalRef where F: FnOnce(&T) -> &U, { @@ -502,7 +502,7 @@ impl GenerationalRef { } /// Filter one ref type to another. - pub fn filter_map(orig: GenerationalRef, f: F) -> Option> + pub fn filter_map(orig: GenerationalRef, f: F) -> Option> where F: FnOnce(&T) -> Option<&U>, { @@ -522,7 +522,7 @@ impl GenerationalRef { } } -impl Deref for GenerationalRef { +impl Deref for GenerationalRef { type Target = T; fn deref(&self) -> &Self::Target { diff --git a/packages/signals/examples/iterator.rs b/packages/signals/examples/iterator.rs index 75875cde2..24db7712f 100644 --- a/packages/signals/examples/iterator.rs +++ b/packages/signals/examples/iterator.rs @@ -16,8 +16,8 @@ fn App(cx: Scope) -> Element { }, "Add one" } - for i in 0..signal().len() { - Child { signal: signal.map(move |v| v.get(i).unwrap()) } + for item in signal.iter_signals() { + Child { signal: item } } } } diff --git a/packages/signals/src/impls.rs b/packages/signals/src/impls.rs index 077777c40..f741b05ed 100644 --- a/packages/signals/src/impls.rs +++ b/packages/signals/src/impls.rs @@ -1,5 +1,6 @@ use crate::rt::CopyValue; use crate::signal::{ReadOnlySignal, Signal, Write}; +use crate::SignalMap; use generational_box::GenerationalRef; use generational_box::GenerationalRefMut; @@ -213,20 +214,20 @@ pub struct CopyValueIterator { value: CopyValue>, } -impl Iterator for CopyValueIterator { - type Item = T; +impl Iterator for CopyValueIterator { + type Item = GenerationalRef; fn next(&mut self) -> Option { let index = self.index; self.index += 1; - self.value.get(index).map(|v| v.clone()) + self.value.get(index) } } -impl IntoIterator for CopyValue> { +impl IntoIterator for CopyValue> { type IntoIter = CopyValueIterator; - type Item = T; + type Item = GenerationalRef; fn into_iter(self) -> Self::IntoIter { CopyValueIterator { @@ -256,20 +257,20 @@ pub struct SignalIterator { value: Signal>, } -impl Iterator for SignalIterator { - type Item = T; +impl Iterator for SignalIterator { + type Item = GenerationalRef; fn next(&mut self) -> Option { let index = self.index; self.index += 1; - self.value.get(index).map(|v| v.clone()) + self.value.get(index) } } -impl IntoIterator for Signal> { +impl IntoIterator for Signal> { type IntoIter = SignalIterator; - type Item = T; + type Item = GenerationalRef; fn into_iter(self) -> Self::IntoIter { SignalIterator { @@ -279,11 +280,37 @@ impl IntoIterator for Signal> { } } +/// An iterator over items in a `Signal>` that yields [`SignalMap`]s. +pub struct MappedSignalIterator { + index: usize, + length: usize, + value: Signal>, +} + +impl Iterator for MappedSignalIterator { + type Item = SignalMap; + + fn next(&mut self) -> Option { + let index = self.index; + self.index += 1; + (index < self.length).then(|| self.value.map(move |v| v.get(index).unwrap())) + } +} + impl Signal> { /// Returns a reference to an element or `None` if out of bounds. pub fn get_mut(&self, index: usize) -> Option>> { Write::filter_map(self.write(), |v| v.get_mut(index)) } + + /// Create an iterator of [`SignalMap`]s over the inner vector. + pub fn iter_signals(&self) -> MappedSignalIterator { + MappedSignalIterator { + index: 0, + length: self.read().len(), + value: *self, + } + } } impl Signal> { diff --git a/packages/signals/src/map.rs b/packages/signals/src/map.rs index 2673fabad..5a9a4a817 100644 --- a/packages/signals/src/map.rs +++ b/packages/signals/src/map.rs @@ -1,14 +1,14 @@ use crate::CopyValue; use crate::Signal; use dioxus_core::ScopeId; -use std::cell::Ref; +use generational_box::GenerationalRef; use std::fmt::Debug; use std::fmt::Display; /// A read only signal that has been mapped to a new type. pub struct SignalMap { origin_scope: ScopeId, - mapping: CopyValue Ref<'static, U>>>, + mapping: CopyValue GenerationalRef>>, } impl SignalMap { @@ -16,7 +16,9 @@ impl SignalMap { pub fn new(signal: Signal, mapping: impl Fn(&T) -> &U + 'static) -> Self { Self { origin_scope: signal.origin_scope(), - mapping: CopyValue::new(Box::new(move || Ref::map(signal.read(), |v| (mapping)(v)))), + mapping: CopyValue::new(Box::new(move || { + GenerationalRef::map(signal.read(), |v| (mapping)(v)) + })), } } @@ -26,7 +28,7 @@ impl SignalMap { } /// Get the current value of the signal. This will subscribe the current scope to the signal. - pub fn read(&self) -> Ref<'static, U> { + pub fn read(&self) -> GenerationalRef { (self.mapping.read())() } @@ -71,8 +73,8 @@ impl Debug for SignalMap { impl SignalMap> { /// Read a value from the inner vector. - pub fn get(&self, index: usize) -> Option> { - Ref::filter_map(self.read(), |v| v.get(index)).ok() + pub fn get(&self, index: usize) -> Option> { + GenerationalRef::filter_map(self.read(), |v| v.get(index)) } } @@ -86,7 +88,40 @@ impl SignalMap> { } /// Attemps to read the inner value of the Option. - pub fn as_ref(&self) -> Option> { - Ref::filter_map(self.read(), |v| v.as_ref()).ok() + pub fn as_ref(&self) -> Option> { + GenerationalRef::filter_map(self.read(), |v| v.as_ref()) + } +} + +impl std::ops::Deref for SignalMap { + type Target = dyn Fn() -> GenerationalRef; + + fn deref(&self) -> &Self::Target { + // https://github.com/dtolnay/case-studies/tree/master/callable-types + + // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit). + let uninit_callable = std::mem::MaybeUninit::::uninit(); + // Then move that value into the closure. We assume that the closure now has a in memory layout of Self. + let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }); + + // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure. + let size_of_closure = std::mem::size_of_val(&uninit_closure); + assert_eq!(size_of_closure, std::mem::size_of::()); + + // Then cast the lifetime of the closure to the lifetime of &self. + fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T { + b + } + let reference_to_closure = cast_lifetime( + { + // The real closure that we will never use. + &uninit_closure + }, + // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self. + unsafe { std::mem::transmute(self) }, + ); + + // Cast the closure to a trait object. + reference_to_closure as &Self::Target } }