diff --git a/examples/all_events.rs b/examples/all_events.rs index d3db35877..bfca23aa8 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -41,9 +41,9 @@ const RECT_STYLE: &str = r#" "#; fn app() -> Element { - let events = use_signal(std::collections::VecDeque::new); + let mut events = use_signal(std::collections::VecDeque::new); - let log_event = move |event: Event| { + let mut log_event = move |event: Event| { let mut events = events.write(); if events.len() >= MAX_EVENTS { diff --git a/examples/calculator.rs b/examples/calculator.rs index 574a8eb5c..c91c242af 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -19,9 +19,9 @@ fn main() { } fn app() -> Element { - let val = use_signal(|| String::from("0")); + let mut val = use_signal(|| String::from("0")); - let input_digit = move |num: u8| { + let mut input_digit = move |num: u8| { if val.cloned() == "0" { val.set(String::new()); } @@ -29,11 +29,11 @@ fn app() -> Element { val.write().push_str(num.to_string().as_str()); }; - let input_operator = move |key: &str| val.write().push_str(key); + let mut input_operator = move |key: &str| val.write().push_str(key); - let handle_key_down_event = move |evt: KeyboardEvent| match evt.key() { + let mut handle_key_down_event = move |evt: KeyboardEvent| match evt.key() { Key::Backspace => { - if !val().is_empty() { + if !val.cloned().is_empty() { val.write().pop(); } } @@ -72,16 +72,16 @@ fn app() -> Element { class: "calculator-key key-clear", onclick: move |_| { val.set(String::new()); - if !val().is_empty(){ + if !val.cloned().is_empty(){ val.set("0".into()); } }, - if val().is_empty() { "C" } else { "AC" } + if val.cloned().is_empty() { "C" } else { "AC" } } button { class: "calculator-key key-sign", onclick: move |_| { - let temp = calc_val(val().as_str()); + let temp = calc_val(val.cloned().as_str()); if temp > 0.0 { val.set(format!("-{temp}")); } else { @@ -120,7 +120,7 @@ fn app() -> Element { button { class: "calculator-key key-add", onclick: move |_| input_operator("+"), "+" } button { class: "calculator-key key-equals", - onclick: move |_| val.set(format!("{}", calc_val(val().as_str()))), + onclick: move |_| val.set(format!("{}", calc_val(val.cloned().as_str()))), "=" } } diff --git a/examples/compose.rs b/examples/compose.rs index 63362d517..be59c5aca 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -10,7 +10,7 @@ fn main() { } fn app() -> Element { - let emails_sent = use_signal(|| Vec::new() as Vec); + let mut emails_sent = use_signal(|| Vec::new() as Vec); // Wait for responses to the compose channel, and then push them to the emails_sent signal. let handle = use_coroutine(|mut rx: UnboundedReceiver| async move { @@ -42,7 +42,7 @@ fn app() -> Element { } fn compose(send: Rc) -> Element { - let user_input = use_signal(String::new); + let mut user_input = use_signal(String::new); rsx! { div { diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 9129018f6..ce7e62d1b 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -7,8 +7,8 @@ fn main() { } fn app() -> Element { - let elements = use_signal(Vec::>::new); - let running = use_signal(|| true); + let mut elements = use_signal(Vec::>::new); + let mut running = use_signal(|| true); use_future(move || async move { let mut focused = 0; diff --git a/examples/counter.rs b/examples/counter.rs index 10fb43773..8308ef07a 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -8,8 +8,8 @@ fn main() { } fn app() -> Element { - let counters = use_signal(|| vec![0, 0, 0]); - let sum = use_selector(move || counters.read().iter().copied().sum::()); + let mut counters = use_signal(|| vec![0, 0, 0]); + let mut sum = use_selector(move || counters.read().iter().copied().sum::()); render! { div { diff --git a/examples/crm.rs b/examples/crm.rs index c352f8e98..e57c8620a 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -58,7 +58,7 @@ pub struct Client { #[component] fn ClientList() -> Element { - let clients = use_context::(); + let mut clients = use_context::(); rsx! { h2 { "List of Clients" } @@ -75,9 +75,9 @@ fn ClientList() -> Element { #[component] fn ClientAdd() -> Element { - let first_name = use_signal(String::new); - let last_name = use_signal(String::new); - let description = use_signal(String::new); + let mut first_name = use_signal(String::new); + let mut last_name = use_signal(String::new); + let mut description = use_signal(String::new); let submit_client = move |_: FormEvent| { consume_context::().write().push(Client { diff --git a/examples/disabled.rs b/examples/disabled.rs index 5e6e7ba2a..7c2913265 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,7 +5,7 @@ fn main() { } fn app() -> Element { - let disabled = use_signal(|| false); + let mut disabled = use_signal(|| false); rsx! { div { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 221f9bbd1..cb45e4211 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -6,8 +6,8 @@ fn main() { } fn app() -> Element { - let breed = use_signal(|| "deerhound".to_string()); - let breed_list = use_future(|| async move { + let mut breed = use_signal(|| "deerhound".to_string()); + let mut breed_list = use_future(|| async move { let list = reqwest::get("https://dog.ceo/api/breeds/list/all") .await .unwrap() diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 8436eb7a4..e153fb8a0 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -20,7 +20,7 @@ fn main() { const _STYLE: &str = manganis::mg!(file("./examples/assets/fileexplorer.css")); fn app() -> Element { - let files = use_signal(Files::new); + let mut files = use_signal(Files::new); rsx! { div { diff --git a/examples/file_upload.rs b/examples/file_upload.rs index da7bf6517..0cc2d2774 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -8,8 +8,8 @@ fn main() { } fn App() -> Element { - let enable_directory_upload = use_signal(|| false); - let files_uploaded = use_signal(|| Vec::new() as Vec); + let mut enable_directory_upload = use_signal(|| false); + let mut files_uploaded = use_signal(|| Vec::new() as Vec); let upload_files = move |evt: FormEvent| async move { for file_name in evt.files().unwrap().files() { diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index c4e59dff9..ae6a93b19 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -38,7 +38,7 @@ fn main() { const STYLE: &str = include_str!("./assets/calculator.css"); fn app() -> Element { - let state = use_signal(Calculator::new); + let mut state = use_signal(Calculator::new); rsx! { style { {STYLE} } diff --git a/examples/pattern_reducer.rs b/examples/pattern_reducer.rs index 0e99f4ca3..4179bb849 100644 --- a/examples/pattern_reducer.rs +++ b/examples/pattern_reducer.rs @@ -12,7 +12,7 @@ fn main() { } fn app() -> Element { - let state = use_signal(|| PlayerState { is_playing: false }); + let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( div { diff --git a/examples/read_size.rs b/examples/read_size.rs index 752d0fdaf..3f13d1c11 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -26,10 +26,10 @@ fn main() { } fn app() -> Element { - let div_element = use_signal(|| None as Option>); - let dimensions = use_signal(Rect::zero); + let mut div_element = use_signal(|| None as Option>); + let mut dimensions = use_signal(Rect::zero); - let read_dims = move |_| async move { + let mut read_dims = move |_| async move { let read = div_element.read(); let client_rect = read.as_ref().map(|el| el.get_client_rect()); if let Some(client_rect) = client_rect { diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index f45bc0101..32a7ac8c0 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -5,7 +5,7 @@ fn main() { } fn app() -> Element { - let header_element = use_signal(|| None); + let mut header_element = use_signal(|| None); rsx! { div { diff --git a/examples/shortcut.rs b/examples/shortcut.rs index 8ff506920..d9bd6e338 100644 --- a/examples/shortcut.rs +++ b/examples/shortcut.rs @@ -6,7 +6,7 @@ fn main() { } fn app() -> Element { - let toggled = use_signal(|| false); + let mut toggled = use_signal(|| false); _ = use_global_shortcut("ctrl+s", move || toggled.toggle()); diff --git a/examples/signals.rs b/examples/signals.rs index d661acbd5..76136519d 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -6,9 +6,9 @@ fn main() { } fn app() -> Element { - let running = dioxus_signals::use_signal(|| true); - let mut count = dioxus_signals::use_signal(|| 0); - let saved_values = dioxus_signals::use_signal(|| vec![0.to_string()]); + let mut running = use_signal(|| true); + let mut count = use_signal(|| 0); + let mut saved_values = use_signal(|| vec![0.to_string()]); // Signals can be used in async functions without an explicit clone since they're 'static and Copy // Signals are backed by a runtime that is designed to deeply integrate with Dioxus apps diff --git a/examples/streams.rs b/examples/streams.rs index a11d10d05..81ac999b8 100644 --- a/examples/streams.rs +++ b/examples/streams.rs @@ -8,7 +8,7 @@ fn main() { } fn app() -> Element { - let count = use_signal(|| 10); + let mut count = use_signal(|| 10); use_future(|| async move { let mut stream = some_stream(); diff --git a/examples/svg.rs b/examples/svg.rs index bfafac889..a87d884b5 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -30,7 +30,7 @@ fn Dice() -> Element { [Y, Y, Y, Y, Y, Y, N], ]; - let value = use_signal(|| 5); + let mut value = use_signal(|| 5); let active_dots = use_selector(move || &DOTS_FOR_VALUE[(value() - 1) as usize]); rsx! { diff --git a/examples/textarea.rs b/examples/textarea.rs index 43f837ea2..2b11d93d0 100644 --- a/examples/textarea.rs +++ b/examples/textarea.rs @@ -7,9 +7,7 @@ fn main() { } fn app() -> Element { - let model = use_signal(|| String::from("asd")); - - println!("{model}"); + let mut model = use_signal(|| String::from("asd")); rsx! { textarea { diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 4d61d23d0..495c2adc4 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -22,8 +22,8 @@ pub struct TodoItem { } pub fn app() -> Element { - let todos = use_signal(|| HashMap::::new()); - let filter = use_signal(|| FilterState::All); + let mut todos = use_signal(|| HashMap::::new()); + let mut filter = use_signal(|| FilterState::All); let active_todo_count = use_selector(move || todos.read().values().filter(|item| !item.checked).count()); @@ -56,17 +56,17 @@ pub fn app() -> Element { class: "toggle-all", r#type: "checkbox", onchange: move |_| { - let check = *active_todo_count() != 0; + let check = active_todo_count() != 0; for (_, item) in todos.write().iter_mut() { item.checked = check; } }, - checked: *active_todo_count() == 0, + checked: active_todo_count() == 0, } label { r#for: "toggle-all" } } ul { class: "todo-list", - for id in filtered_todos().iter().copied() { + for id in filtered_todos.read().iter().copied() { TodoEntry { key: "{id}", id, todos } } } @@ -81,7 +81,8 @@ pub fn app() -> Element { #[component] pub fn TodoHeader(todos: Signal>) -> Element { - let draft = use_signal(|| "".to_string()); + let mut todos = todos; + let mut draft = use_signal(|| "".to_string()); let mut todo_id = use_signal(|| 0); let onkeydown = move |evt: KeyboardEvent| { @@ -115,7 +116,8 @@ pub fn TodoHeader(todos: Signal>) -> Element { #[component] pub fn TodoEntry(todos: Signal>, id: u32) -> Element { - let is_editing = use_signal(|| false); + let mut todos = todos; + let mut is_editing = use_signal(|| false); let checked = use_selector(move || todos.read().get(&id).unwrap().checked); let contents = use_selector(move || todos.read().get(&id).unwrap().contents.clone()); @@ -166,6 +168,8 @@ pub fn ListFooter( active_todo_count: ReadOnlySignal, filter: Signal, ) -> Element { + let mut todos = todos; + let mut filter = filter; let show_clear_completed = use_selector(move || todos.read().values().any(|todo| todo.checked)); rsx! { @@ -173,7 +177,7 @@ pub fn ListFooter( span { class: "todo-count", strong { "{active_todo_count} " } span { - match *active_todo_count() { + match active_todo_count() { 1 => "item", _ => "items", } @@ -197,7 +201,7 @@ pub fn ListFooter( } } } - if *show_clear_completed() { + if show_clear_completed() { button { class: "clear-completed", onclick: move |_| todos.write().retain(|_, todo| !todo.checked), diff --git a/examples/window_event.rs b/examples/window_event.rs index 6d6f790b7..f397c4451 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -12,9 +12,9 @@ fn main() { } fn app() -> Element { - let fullscreen = use_signal(|| false); - let always_on_top = use_signal(|| false); - let decorations = use_signal(|| false); + let mut fullscreen = use_signal(|| false); + let mut always_on_top = use_signal(|| false); + let mut decorations = use_signal(|| false); rsx!( link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" } diff --git a/examples/window_focus.rs b/examples/window_focus.rs index a6f7120cd..d5641215e 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -11,7 +11,7 @@ fn main() { } fn app() -> Element { - let focused = use_signal(|| false); + let mut focused = use_signal(|| false); use_wry_event_handler(move |event, _| match event { WryEvent::WindowEvent { diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index 1a7607df2..44991fdec 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -5,7 +5,7 @@ fn main() { } fn app() -> Element { - let level = use_signal(|| 1.0); + let mut level = use_signal(|| 1.0); rsx! { input { diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index 7407b7227..bc58c1ddd 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -9,7 +9,7 @@ fn main() { } fn app() -> Element { - let contents = use_signal(|| String::from("")); + let mut contents = use_signal(|| String::from("")); rsx! { div { diff --git a/packages/generational-box/src/lib.rs b/packages/generational-box/src/lib.rs index e050ee330..65298520a 100644 --- a/packages/generational-box/src/lib.rs +++ b/packages/generational-box/src/lib.rs @@ -477,13 +477,13 @@ impl Display for AlreadyBorrowedError { impl std::error::Error for AlreadyBorrowedError {} /// A reference to a value in a generational box. -pub struct GenerationalRef { - inner: Ref<'static, T>, +pub struct GenerationalRef<'a, T: 'static> { + inner: Ref<'a, T>, #[cfg(any(debug_assertions, feature = "debug_borrows"))] borrow: GenerationalRefBorrowInfo, } -impl GenerationalRef { +impl<'a, T: 'static> GenerationalRef<'a, T> { /// Map one ref type to another. pub fn map(orig: GenerationalRef, f: F) -> GenerationalRef where @@ -500,7 +500,7 @@ impl GenerationalRef { } /// Filter one ref type to another. - pub fn filter_map(orig: GenerationalRef, f: F) -> Option> + pub fn filter_map(orig: GenerationalRef<'a, T>, f: F) -> Option> where F: FnOnce(&T) -> Option<&U>, { @@ -520,7 +520,7 @@ impl GenerationalRef { } } -impl Deref for GenerationalRef { +impl Deref for GenerationalRef<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -545,13 +545,13 @@ impl Drop for GenerationalRefBorrowInfo { } /// A mutable reference to a value in a generational box. -pub struct GenerationalRefMut { - inner: RefMut<'static, T>, +pub struct GenerationalRefMut<'a, T: 'static> { + inner: RefMut<'a, T>, #[cfg(any(debug_assertions, feature = "debug_borrows"))] borrow: GenerationalRefMutBorrowInfo, } -impl GenerationalRefMut { +impl<'a, T: 'static> GenerationalRefMut<'a, T> { /// Map one ref type to another. pub fn map(orig: GenerationalRefMut, f: F) -> GenerationalRefMut where @@ -565,7 +565,10 @@ impl GenerationalRefMut { } /// Filter one ref type to another. - pub fn filter_map(orig: GenerationalRefMut, f: F) -> Option> + pub fn filter_map( + orig: GenerationalRefMut<'a, T>, + f: F, + ) -> Option> where F: FnOnce(&mut T) -> Option<&mut U>, { @@ -584,7 +587,7 @@ impl GenerationalRefMut { } } -impl Deref for GenerationalRefMut { +impl<'a, T: 'static> Deref for GenerationalRefMut<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -592,7 +595,7 @@ impl Deref for GenerationalRefMut { } } -impl DerefMut for GenerationalRefMut { +impl<'a, T: 'static> DerefMut for GenerationalRefMut<'a, T> { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.deref_mut() } diff --git a/packages/hooks/src/use_coroutine.rs b/packages/hooks/src/use_coroutine.rs index c49b7eac3..04bb655f7 100644 --- a/packages/hooks/src/use_coroutine.rs +++ b/packages/hooks/src/use_coroutine.rs @@ -125,7 +125,7 @@ impl Coroutine { /// Restart this coroutine /// /// Forces the component to re-render, which will re-invoke the coroutine. - pub fn restart(&self) { + pub fn restart(&mut self) { self.needs_regen.set(true); self.task().stop(); } diff --git a/packages/hooks/src/use_future.rs b/packages/hooks/src/use_future.rs index 418052076..314516cd8 100644 --- a/packages/hooks/src/use_future.rs +++ b/packages/hooks/src/use_future.rs @@ -25,8 +25,8 @@ where T: 'static, F: Future + 'static, { - let value = use_signal(|| None); - let state = use_signal(|| UseFutureState::Pending); + let mut value = use_signal(|| None); + let mut state = use_signal(|| UseFutureState::Pending); let task = use_signal(|| { // Create the user's task @@ -84,7 +84,7 @@ impl UseFuture { } // Manually set the value in the future slot without starting the future over - pub fn set(&self, new_value: T) { + pub fn set(&mut self, new_value: T) { self.value.set(Some(new_value)); } diff --git a/packages/signals/src/impls.rs b/packages/signals/src/impls.rs index 077777c40..6e1d8567e 100644 --- a/packages/signals/src/impls.rs +++ b/packages/signals/src/impls.rs @@ -120,78 +120,81 @@ macro_rules! write_impls { impl $ty> { /// Pushes a new value to the end of the vector. - pub fn push(&self, value: T) { + pub fn push(&mut self, value: T) { self.with_mut(|v| v.push(value)) } /// Pops the last value from the vector. - pub fn pop(&self) -> Option { + pub fn pop(&mut self) -> Option { self.with_mut(|v| v.pop()) } /// Inserts a new value at the given index. - pub fn insert(&self, index: usize, value: T) { + pub fn insert(&mut self, index: usize, value: T) { self.with_mut(|v| v.insert(index, value)) } /// Removes the value at the given index. - pub fn remove(&self, index: usize) -> T { + pub fn remove(&mut self, index: usize) -> T { self.with_mut(|v| v.remove(index)) } /// Clears the vector, removing all values. - pub fn clear(&self) { + pub fn clear(&mut self) { self.with_mut(|v| v.clear()) } /// Extends the vector with the given iterator. - pub fn extend(&self, iter: impl IntoIterator) { + pub fn extend(&mut self, iter: impl IntoIterator) { self.with_mut(|v| v.extend(iter)) } /// Truncates the vector to the given length. - pub fn truncate(&self, len: usize) { + pub fn truncate(&mut self, len: usize) { self.with_mut(|v| v.truncate(len)) } /// Swaps two values in the vector. - pub fn swap_remove(&self, index: usize) -> T { + pub fn swap_remove(&mut self, index: usize) -> T { self.with_mut(|v| v.swap_remove(index)) } /// Retains only the values that match the given predicate. - pub fn retain(&self, f: impl FnMut(&T) -> bool) { + pub fn retain(&mut self, f: impl FnMut(&T) -> bool) { self.with_mut(|v| v.retain(f)) } /// Splits the vector into two at the given index. - pub fn split_off(&self, at: usize) -> Vec { + pub fn split_off(&mut self, at: usize) -> Vec { self.with_mut(|v| v.split_off(at)) } } impl $ty> { /// Takes the value out of the Option. - pub fn take(&self) -> Option { + pub fn take(&mut self) -> Option { self.with_mut(|v| v.take()) } /// Replace the value in the Option. - pub fn replace(&self, value: T) -> Option { + pub fn replace(&mut self, value: T) -> Option { self.with_mut(|v| v.replace(value)) } /// Gets the value out of the Option, or inserts the given value if the Option is empty. - pub fn get_or_insert(&self, default: T) -> GenerationalRef { + pub fn get_or_insert(&mut self, default: T) -> GenerationalRef { 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. - pub fn get_or_insert_with(&self, default: impl FnOnce() -> T) -> GenerationalRef { + pub fn get_or_insert_with( + &mut self, + default: impl FnOnce() -> T, + ) -> GenerationalRef { let borrow = self.read(); if borrow.is_none() { drop(borrow); - self.with_mut(|v| *v = Some(default())); + self.write_unchecked().replace(default()); GenerationalRef::map(self.read(), |v| v.as_ref().unwrap()) } else { GenerationalRef::map(borrow, |v| v.as_ref().unwrap()) @@ -281,14 +284,14 @@ impl IntoIterator for Signal> { impl Signal> { /// Returns a reference to an element or `None` if out of bounds. - pub fn get_mut(&self, index: usize) -> Option>> { + pub fn get_mut(&mut self, index: usize) -> Option>> { Write::filter_map(self.write(), |v| v.get_mut(index)) } } impl Signal> { /// Returns a reference to an element or `None` if out of bounds. - pub fn as_mut(&self) -> Option>> { + pub fn as_mut(&mut self) -> Option>> { Write::filter_map(self.write(), |v| v.as_mut()) } } diff --git a/packages/signals/src/rt.rs b/packages/signals/src/rt.rs index ae1f13155..845619e4d 100644 --- a/packages/signals/src/rt.rs +++ b/packages/signals/src/rt.rs @@ -151,6 +151,10 @@ impl CopyValue { self.value.try_write() } + pub fn write_unchecked(&self) -> GenerationalRefMut { + self.value.write() + } + /// Write the value. If the value has been dropped, this will panic. #[track_caller] pub fn write(&self) -> GenerationalRefMut { @@ -188,8 +192,8 @@ impl PartialEq for CopyValue { } } -impl Deref for CopyValue { - type Target = dyn Fn() -> GenerationalRef; +impl Deref for CopyValue { + type Target = dyn Fn() -> T; fn deref(&self) -> &Self::Target { // https://github.com/dtolnay/case-studies/tree/master/callable-types @@ -197,7 +201,7 @@ impl Deref for CopyValue { // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit). let uninit_callable = 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() }); + let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone(); // 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); diff --git a/packages/signals/src/selector.rs b/packages/signals/src/selector.rs index e10fe41b3..3f3ce00a2 100644 --- a/packages/signals/src/selector.rs +++ b/packages/signals/src/selector.rs @@ -26,49 +26,11 @@ pub fn use_selector(f: impl FnMut() -> R + 'static) -> ReadOnlySig use_hook(|| selector(f)) } -/// Creates a new Selector with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes -/// -/// Selectors can be used to efficiently compute derived data from signals. -/// -/// ```rust -/// use dioxus::prelude::*; -/// use dioxus_signals::*; -/// -/// fn App() -> Element { -/// let mut local_state = use_signal(|| 0); -/// let double = use_selector_with_dependencies((local_state.get(),), move |(local_state,)| local_state * 2); -/// local_state.set(1); -/// -/// render! { "{double}" } -/// } -/// ``` -#[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"] -pub fn use_selector_with_dependencies( - dependencies: D, - mut f: impl FnMut(D::Out) -> R + 'static, -) -> ReadOnlySignal -where - D::Out: 'static, -{ - let dependencies_signal = use_signal(|| dependencies.out()); - let selector = use_hook(|| { - selector(move || { - let deref = &*dependencies_signal.read(); - f(deref.clone()) - }) - }); - let changed = { dependencies.changed(&*dependencies_signal.read()) }; - if changed { - dependencies_signal.set(dependencies.out()); - } - selector -} - /// Creates a new Selector. The selector will be run immediately and whenever any signal it reads changes. /// /// Selectors can be used to efficiently compute derived data from signals. pub fn selector(mut f: impl FnMut() -> R + 'static) -> ReadOnlySignal { - let state = Signal:: { + let mut state = Signal:: { inner: CopyValue::invalid(), }; let effect = Effect { diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index 9e9c0077e..604ee805b 100644 --- a/packages/signals/src/signal.rs +++ b/packages/signals/src/signal.rs @@ -210,7 +210,7 @@ impl Signal { /// /// If the signal has been dropped, this will panic. #[track_caller] - pub fn read(&self) -> GenerationalRef { + pub fn read<'a>(&'a self) -> GenerationalRef<'a, T> { let inner = self.inner.read(); if let Some(effect) = inner.effect_stack.current() { let mut effect_subscribers = inner.effect_subscribers.borrow_mut(); @@ -245,11 +245,8 @@ impl Signal { GenerationalRef::map(inner, |v| &v.value) } - /// Get a mutable reference to the signal's value. - /// - /// If the signal has been dropped, this will panic. - #[track_caller] - pub fn write(&self) -> Write { + /// Write to the value through an immutable reference. + pub fn write_unchecked(&self) -> Write { let inner = self.inner.write(); let borrow = GenerationalRefMut::map(inner, |v| &mut v.value); Write { @@ -258,6 +255,14 @@ impl Signal { } } + /// Get a mutable reference to the signal's value. + /// + /// If the signal has been dropped, this will panic. + #[track_caller] + pub fn write<'a>(&'a mut self) -> Write<'a, T> { + self.write_unchecked() + } + fn update_subscribers(&self) { { let inner = self.inner.read(); @@ -288,7 +293,7 @@ impl Signal { /// Set the value of the signal. This will trigger an update on all subscribers. #[track_caller] - pub fn set(&self, value: T) { + pub fn set(&mut self, value: T) { *self.write() = value; } @@ -311,7 +316,7 @@ impl Signal { /// Run a closure with a mutable reference to the signal's value. /// If the signal has been dropped, this will panic. #[track_caller] - pub fn with_mut(&self, f: impl FnOnce(&mut T) -> O) -> O { + pub fn with_mut(&mut self, f: impl FnOnce(&mut T) -> O) -> O { let mut write = self.write(); f(&mut *write) } @@ -337,7 +342,7 @@ impl Signal { impl Signal { /// Invert the boolean value of the signal. This will trigger an update on all subscribers. - pub fn toggle(&self) { + pub fn toggle(&mut self) { self.set(!self.cloned()); } } @@ -351,7 +356,7 @@ impl PartialEq for Signal { /// Allow calling a signal with signal() syntax /// /// Currently only limited to copy types, though could probably specialize for string/arc/rc -impl Deref for Signal { +impl Deref for Signal { type Target = dyn Fn() -> T; fn deref(&self) -> &Self::Target { @@ -360,10 +365,7 @@ impl Deref for Signal { // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit). let uninit_callable = 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 || { - let res = Self::read(unsafe { &*uninit_callable.as_ptr() }); - res.clone() - }; + let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone(); // 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); @@ -398,14 +400,14 @@ impl Drop for SignalSubscriberDrop { } /// A mutable reference to a signal's value. -pub struct Write { - write: GenerationalRefMut, +pub struct Write<'a, T: 'static, I: 'static = T> { + write: GenerationalRefMut<'a, T>, signal: SignalSubscriberDrop, } -impl Write { +impl<'a, T: 'static, I: 'static> Write<'a, T, I> { /// Map the mutable reference to the signal's value to a new type. - pub fn map(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write { + pub fn map(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> { let Self { write, signal } = myself; Write { write: GenerationalRefMut::map(write, f), @@ -417,14 +419,14 @@ impl Write { pub fn filter_map( myself: Self, f: impl FnOnce(&mut T) -> Option<&mut O>, - ) -> Option> { + ) -> Option> { let Self { write, signal } = myself; let write = GenerationalRefMut::filter_map(write, f); write.map(|write| Write { write, signal }) } } -impl Deref for Write { +impl<'a, T: 'static, I: 'static> Deref for Write<'a, T, I> { type Target = T; fn deref(&self) -> &Self::Target { @@ -432,7 +434,7 @@ impl Deref for Write { } } -impl DerefMut for Write { +impl<'a, T, I> DerefMut for Write<'a, T, I> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.write } @@ -489,7 +491,7 @@ impl PartialEq for ReadOnlySignal { } } -impl Deref for ReadOnlySignal { +impl Deref for ReadOnlySignal { type Target = dyn Fn() -> T; fn deref(&self) -> &Self::Target {