From 32df544309b43c943f544044b459e33ce31f7cba Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 11:46:09 +0100 Subject: [PATCH 01/28] Use cheap-to-clone types more --- examples/nested_list/src/list.rs | 11 ++++++----- packages/yew/src/utils/mod.rs | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/nested_list/src/list.rs b/examples/nested_list/src/list.rs index 042227750df..774807321fd 100644 --- a/examples/nested_list/src/list.rs +++ b/examples/nested_list/src/list.rs @@ -1,5 +1,6 @@ use std::rc::Rc; +use implicit_clone::unsync::IArray; use yew::prelude::*; use yew::virtual_dom::VChild; @@ -14,9 +15,9 @@ pub enum Msg { #[derive(Clone, PartialEq, Properties)] pub struct Props { #[prop_or_default] - pub header: Vec>, + pub header: IArray>, #[prop_or_default] - pub children: Vec>, + pub children: IArray>, pub on_hover: Callback, pub weak_link: WeakComponentLink, @@ -56,7 +57,7 @@ impl Component for List { html! {
- { ctx.props().header.clone() } + { &ctx.props().header }
{ Self::view_items(ctx.props().children.clone()) }
@@ -67,9 +68,9 @@ impl Component for List { } impl List { - fn view_items(children: Vec>) -> Html { + fn view_items(children: IArray>) -> Html { children - .into_iter() + .iter() .filter(|c| !c.props.hide) .enumerate() .map(|(i, mut c)| { diff --git a/packages/yew/src/utils/mod.rs b/packages/yew/src/utils/mod.rs index 4f33ea672b2..e3c1a29d080 100644 --- a/packages/yew/src/utils/mod.rs +++ b/packages/yew/src/utils/mod.rs @@ -1,5 +1,7 @@ //! This module contains useful utilities to get information about the current document. +use implicit_clone::unsync::IArray; +use implicit_clone::ImplicitClone; use std::marker::PhantomData; use yew::html::ChildrenRenderer; From fe3636d3dad9b13019b07e6a3c1db4e6c0bf83f5 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 11:57:39 +0100 Subject: [PATCH 02/28] Use IArray in NodeSeq & add IntoPropValue for &AttrValue --- Cargo.lock | 5 +- examples/nested_list/Cargo.toml | 1 + examples/timer/src/main.rs | 22 +++++--- packages/yew/Cargo.toml | 2 +- .../src/html/conversion/into_prop_value.rs | 1 + packages/yew/src/utils/mod.rs | 50 ++++++++++++++----- 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b560bd7eaef..747ec22c3df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1675,9 +1675,9 @@ dependencies = [ [[package]] name = "implicit-clone" -version = "0.4.1" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3d77000817fd9e7db159e8d52ed9b5941a2cdbfbdc8ca646e59887ae2b2dd1" +checksum = "789a53d49d2276908b1fcccb9dfcf65c7a6e884c1df639f26aeff4f142b3ef9e" dependencies = [ "indexmap 2.0.2", ] @@ -2088,6 +2088,7 @@ dependencies = [ name = "nested_list" version = "0.1.0" dependencies = [ + "implicit-clone", "log", "wasm-logger", "yew", diff --git a/examples/nested_list/Cargo.toml b/examples/nested_list/Cargo.toml index 04e6bcf6913..8f206cf7b70 100644 --- a/examples/nested_list/Cargo.toml +++ b/examples/nested_list/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" license = "MIT OR Apache-2.0" [dependencies] +implicit-clone = "0.4" log = "0.4" wasm-logger = "0.2" yew = { path = "../../packages/yew", features = ["csr"] } diff --git a/examples/timer/src/main.rs b/examples/timer/src/main.rs index 241f86a9805..742b1200c3f 100644 --- a/examples/timer/src/main.rs +++ b/examples/timer/src/main.rs @@ -1,6 +1,6 @@ use gloo::console::{self, Timer}; use gloo::timers::callback::{Interval, Timeout}; -use yew::{html, Component, Context, Html}; +use yew::prelude::*; pub enum Msg { StartTimeout, @@ -13,7 +13,7 @@ pub enum Msg { pub struct App { time: String, - messages: Vec<&'static str>, + messages: Vec, _standalone: (Interval, Interval), interval: Option, timeout: Option, @@ -68,7 +68,7 @@ impl Component for App { self.messages.clear(); console::clear!(); - self.messages.push("Timer started!"); + self.log("Timer started!"); self.console_timer = Some(Timer::new("Timer")); true } @@ -82,18 +82,18 @@ impl Component for App { self.messages.clear(); console::clear!(); - self.messages.push("Interval started!"); + self.log("Interval started!"); true } Msg::Cancel => { self.cancel(); - self.messages.push("Canceled!"); + self.log("Canceled!"); console::warn!("Canceled!"); true } Msg::Done => { self.cancel(); - self.messages.push("Done!"); + self.log("Done!"); // todo weblog // ConsoleService::group(); @@ -107,7 +107,7 @@ impl Component for App { true } Msg::Tick => { - self.messages.push("Tick..."); + self.log("Tick..."); // todo weblog // ConsoleService::count_named("Tick"); true @@ -139,7 +139,7 @@ impl Component for App { { &self.time }
- { for self.messages.iter().map(|message| html! {

{ *message }

}) } + { for self.messages.iter().map(|message| html! {

{ message }

}) }
@@ -147,6 +147,12 @@ impl Component for App { } } +impl App { + fn log(&mut self, message: impl Into) { + self.messages.push(message.into()); + } +} + fn main() { yew::Renderer::::new().render(); } diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index 89cd95b4c6c..e896e34c6b7 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -27,7 +27,7 @@ yew-macro = { version = "^0.21.0", path = "../yew-macro" } thiserror = "1.0" futures = { version = "0.3", default-features = false, features = ["std"] } html-escape = { version = "0.2.13", optional = true } -implicit-clone = { version = "0.4.1", features = ["map"] } +implicit-clone = { version = "0.4.5", features = ["map"] } base64ct = { version = "1.6.0", features = ["std"], optional = true } bincode = { version = "1.3.3", optional = true } serde = { version = "1", features = ["derive"] } diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 9b2972b5b93..4b96129cd24 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -326,6 +326,7 @@ impl_into_prop_value_via_display!(f64); impl_into_prop_value_via_attr_value!(String); impl_into_prop_value_via_attr_value!(AttrValue); +impl_into_prop_value_via_attr_value!(&AttrValue); impl_into_prop_value_via_attr_value!(Rc); impl_into_prop_value_via_attr_value!(Cow<'static, str>); diff --git a/packages/yew/src/utils/mod.rs b/packages/yew/src/utils/mod.rs index e3c1a29d080..76b94df5cd8 100644 --- a/packages/yew/src/utils/mod.rs +++ b/packages/yew/src/utils/mod.rs @@ -17,38 +17,64 @@ where /// A special type necessary for flattening components returned from nested html macros. #[derive(Debug)] -pub struct NodeSeq(Vec, PhantomData); +pub struct NodeSeq(IArray, PhantomData); -impl, OUT> From for NodeSeq { +impl, OUT: ImplicitClone + 'static> From for NodeSeq { fn from(val: IN) -> Self { - Self(vec![val.into()], PhantomData) + Self(IArray::Single([val.into()]), PhantomData) } } -impl, OUT> From> for NodeSeq { +impl, OUT: ImplicitClone + 'static> From> for NodeSeq { fn from(val: Option) -> Self { - Self(val.map(|s| vec![s.into()]).unwrap_or_default(), PhantomData) + Self( + val.map(|s| IArray::Single([s.into()])).unwrap_or_default(), + PhantomData, + ) } } -impl, OUT> From> for NodeSeq { - fn from(val: Vec) -> Self { - Self(val.into_iter().map(|x| x.into()).collect(), PhantomData) +impl, OUT: ImplicitClone + 'static> From> for NodeSeq { + fn from(mut val: Vec) -> Self { + if val.len() == 1 { + let item = val.pop().unwrap(); + Self(IArray::Single([item.into()]), PhantomData) + } else { + Self(val.into_iter().map(|x| x.into()).collect(), PhantomData) + } } } -impl + Clone, OUT> From<&ChildrenRenderer> for NodeSeq { +impl + ImplicitClone, OUT: ImplicitClone + 'static> From> + for NodeSeq +{ + fn from(val: IArray) -> Self { + Self(val.iter().map(|x| x.into()).collect(), PhantomData) + } +} + +impl + ImplicitClone, OUT: ImplicitClone + 'static> From<&IArray> + for NodeSeq +{ + fn from(val: &IArray) -> Self { + Self(val.iter().map(|x| x.into()).collect(), PhantomData) + } +} + +impl + Clone, OUT: ImplicitClone + 'static> From<&ChildrenRenderer> + for NodeSeq +{ fn from(val: &ChildrenRenderer) -> Self { Self(val.iter().map(|x| x.into()).collect(), PhantomData) } } -impl IntoIterator for NodeSeq { - type IntoIter = std::vec::IntoIter; +impl IntoIterator for NodeSeq { + type IntoIter = implicit_clone::unsync::Iter; type Item = OUT; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + self.0.iter() } } From d0cfdcb7d67bbdf6c93bc78efc427175cfaa2c1c Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 15:05:58 +0100 Subject: [PATCH 03/28] Add function get_mut() on VChild to make things easier --- examples/nested_list/src/list.rs | 4 +--- packages/yew/src/virtual_dom/vcomp.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/nested_list/src/list.rs b/examples/nested_list/src/list.rs index 774807321fd..d2639b6611a 100644 --- a/examples/nested_list/src/list.rs +++ b/examples/nested_list/src/list.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use implicit_clone::unsync::IArray; use yew::prelude::*; use yew::virtual_dom::VChild; @@ -74,7 +72,7 @@ impl List { .filter(|c| !c.props.hide) .enumerate() .map(|(i, mut c)| { - let props = Rc::make_mut(&mut c.props); + let props = c.get_mut(); props.name = format!("#{} - {}", i + 1, props.name).into(); c }) diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs index 07c5faba0a1..78b091fbe63 100644 --- a/packages/yew/src/virtual_dom/vcomp.rs +++ b/packages/yew/src/virtual_dom/vcomp.rs @@ -216,6 +216,17 @@ where } } +impl VChild +where + COMP: BaseComponent, + COMP::Properties: Clone, +{ + /// Get a mutable reference to the underlying properties. + pub fn get_mut(&mut self) -> &mut COMP::Properties { + Rc::make_mut(&mut self.props) + } +} + impl From> for VComp where COMP: BaseComponent, From 77b2656398245770e1d4db1f276dfdc269fbaae1 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 15:17:53 +0100 Subject: [PATCH 04/28] Use AttrValue in timer_functional example --- examples/timer_functional/src/main.rs | 39 +++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/examples/timer_functional/src/main.rs b/examples/timer_functional/src/main.rs index 5a717cb5476..4f2ffc067ae 100644 --- a/examples/timer_functional/src/main.rs +++ b/examples/timer_functional/src/main.rs @@ -18,11 +18,34 @@ enum TimerAction { #[derive(Clone, Debug)] struct TimerState { - messages: Vec<&'static str>, + messages: Messages, interval_handle: Option>, timeout_handle: Option>, } +#[derive(Clone, Debug, Default, PartialEq, Eq)] +struct Messages(Vec); + +impl Messages { + fn log(&mut self, message: impl Into) { + self.0.push(message.into()); + } +} + +impl std::ops::Deref for Messages { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromIterator<&'static str> for Messages { + fn from_iter>(it: T) -> Self { + Messages(it.into_iter().map(Into::into).collect()) + } +} + impl PartialEq for TimerState { fn eq(&self, other: &Self) -> bool { self.messages == other.messages @@ -37,7 +60,7 @@ impl Reducible for TimerState { match action { TimerAction::Add(message) => { let mut messages = self.messages.clone(); - messages.push(message); + messages.log(message); Rc::new(TimerState { messages, interval_handle: self.interval_handle.clone(), @@ -45,18 +68,18 @@ impl Reducible for TimerState { }) } TimerAction::SetInterval(t) => Rc::new(TimerState { - messages: vec!["Interval started!"], + messages: ["Interval started!"].into_iter().collect(), interval_handle: Some(Rc::from(t)), timeout_handle: self.timeout_handle.clone(), }), TimerAction::SetTimeout(t) => Rc::new(TimerState { - messages: vec!["Timer started!!"], + messages: ["Timer started!!"].into_iter().collect(), interval_handle: self.interval_handle.clone(), timeout_handle: Some(Rc::from(t)), }), TimerAction::TimeoutDone => { let mut messages = self.messages.clone(); - messages.push("Done!"); + messages.log("Done!"); Rc::new(TimerState { messages, interval_handle: self.interval_handle.clone(), @@ -65,7 +88,7 @@ impl Reducible for TimerState { } TimerAction::Cancel => { let mut messages = self.messages.clone(); - messages.push("Canceled!"); + messages.log("Canceled!"); Rc::new(TimerState { messages, interval_handle: None, @@ -94,7 +117,7 @@ fn clock() -> Html { #[function_component] fn App() -> Html { let state = use_reducer(|| TimerState { - messages: Vec::new(), + messages: Default::default(), interval_handle: None, timeout_handle: None, }); @@ -105,7 +128,7 @@ fn App() -> Html { .iter() .map(|message| { key += 1; - html! {

{ *message }

} + html! {

{ message }

} }) .collect(); From 71bcd09af09de146aaf1d9adcec932eaab7b1088 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 17:59:10 +0100 Subject: [PATCH 05/28] Allow iterating over ref of ChildrenRenderer --- packages/yew/src/html/component/children.rs | 72 ++++++++++++++----- .../src/html/conversion/into_prop_value.rs | 33 +++++---- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index 701f00fd9df..63b44f008ba 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -3,7 +3,10 @@ use std::fmt; use std::rc::Rc; +use implicit_clone::{unsync::IArray, ImplicitClone}; + use crate::html::Html; +use crate::utils::RcExt; use crate::virtual_dom::{VChild, VComp, VList, VNode}; use crate::{BaseComponent, Properties}; @@ -152,11 +155,13 @@ pub type ChildrenWithProps = ChildrenRenderer>; /// A type used for rendering children html. #[derive(Clone)] -pub struct ChildrenRenderer { - pub(crate) children: Vec, +pub struct ChildrenRenderer { + pub(crate) children: IArray>, } -impl PartialEq for ChildrenRenderer { +impl ImplicitClone for ChildrenRenderer {} + +impl PartialEq for ChildrenRenderer { fn eq(&self, other: &Self) -> bool { self.children == other.children } @@ -164,11 +169,13 @@ impl PartialEq for ChildrenRenderer { impl ChildrenRenderer where - T: Clone, + T: Clone + 'static, { /// Create children pub fn new(children: Vec) -> Self { - Self { children } + Self { + children: children.into_iter().map(Rc::new).collect(), + } } /// Children list is empty @@ -185,7 +192,8 @@ where pub fn iter(&self) -> impl Iterator + '_ { // clone each child lazily. // This way `self.iter().next()` only has to clone a single node. - self.children.iter().cloned() + // TODO not sure if I shouldnt keep the Rc here + self.children.iter().map(RcExt::unwrap_or_clone) } /// Convert the children elements to another object (if there are any). @@ -197,7 +205,7 @@ where /// children.map(|children| { /// html! { ///
- /// {children.clone()} + /// {children} ///
/// } /// }) @@ -210,37 +218,63 @@ where closure(self) } } + + pub(crate) fn to_vec(&self) -> Vec { + self.iter().collect() + } } -impl Default for ChildrenRenderer { +impl Default for ChildrenRenderer { fn default() -> Self { Self { - children: Vec::new(), + children: Default::default(), } } } -impl fmt::Debug for ChildrenRenderer { +impl fmt::Debug for ChildrenRenderer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("ChildrenRenderer<_>") } } -impl IntoIterator for ChildrenRenderer { - type IntoIter = std::vec::IntoIter; +#[derive(Debug)] +#[doc(hidden)] +pub struct Iter { + children: implicit_clone::unsync::Iter>, +} + +impl Iterator for Iter { + type Item = T; + + fn next(&mut self) -> Option { + self.children.next().map(|x| RcExt::unwrap_or_clone(x)) + } +} + +impl IntoIterator for ChildrenRenderer { + type IntoIter = Iter; type Item = T; fn into_iter(self) -> Self::IntoIter { - self.children.into_iter() + Iter { + children: self.children.iter(), + } + } +} + +impl FromIterator for ChildrenRenderer { + fn from_iter>(it: IT) -> Self { + Self { + children: it.into_iter().map(Rc::new).collect(), + } } } impl From> for Html { - fn from(mut val: ChildrenRenderer) -> Self { + fn from(val: ChildrenRenderer) -> Self { if val.children.len() == 1 { - if let Some(m) = val.children.pop() { - return m; - } + return RcExt::unwrap_or_clone(val.children[0].clone()); } Html::VList(Rc::new(val.into())) @@ -252,7 +286,7 @@ impl From> for VList { if val.is_empty() { return VList::new(); } - VList::with_children(val.children, None) + VList::with_children(val.to_vec(), None) } } @@ -285,7 +319,7 @@ mod tests { #[test] fn children_map() { - let children = Children::new(vec![]); + let children = Children::new(Default::default()); let res = children.map(|children| Some(children.clone())); assert!(res.is_none()); let children = Children::new(vec![Default::default()]); diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 4b96129cd24..988ca79cc90 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -86,47 +86,47 @@ where impl IntoPropValue> for VChild where T: BaseComponent, - C: Clone + Into, + C: ImplicitClone + Into, VChild: Into, { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - ChildrenRenderer::new(vec![self.into()]) + [self.into()].into_iter().collect() } } impl IntoPropValue>> for VChild where T: BaseComponent, - C: Clone + Into, + C: ImplicitClone + Into, VChild: Into, { #[inline] fn into_prop_value(self) -> Option> { - Some(ChildrenRenderer::new(vec![self.into()])) + Some([self.into()].into_iter().collect()) } } impl IntoPropValue>> for Option> where T: BaseComponent, - C: Clone + Into, + C: ImplicitClone + Into, VChild: Into, { #[inline] fn into_prop_value(self) -> Option> { - self.map(|m| ChildrenRenderer::new(vec![m.into()])) + self.map(|m| [m.into()].into_iter().collect()) } } impl IntoPropValue> for Vec where T: Into, - R: Clone + Into, + R: ImplicitClone + Into, { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - ChildrenRenderer::new(self.into_iter().map(|m| m.into()).collect()) + self.into_iter().map(|m| m.into()).collect() } } @@ -167,24 +167,33 @@ impl IntoPropValue for ChildrenRenderer { } } +impl IntoPropValue for &ChildrenRenderer { + #[inline] + fn into_prop_value(self) -> VNode { + VNode::VList(Rc::new(self.clone().into())) + } +} + impl IntoPropValue> for VNode { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - ChildrenRenderer::new(vec![self]) + [self].into_iter().collect() } } impl IntoPropValue> for VText { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - ChildrenRenderer::new(vec![self.into()]) + [self.into()].into_iter().collect() } } impl IntoPropValue for ChildrenRenderer { #[inline] fn into_prop_value(self) -> VList { - VList::with_children(self.children, None) + // TODO converting IArray to Vec doesn't seem optimal, but it's too complicated to change + // VList for now + VList::with_children(self.to_vec(), None) } } @@ -197,7 +206,7 @@ impl IntoPropValue for VChild { impl IntoPropValue> for AttrValue { fn into_prop_value(self) -> ChildrenRenderer { - ChildrenRenderer::new(vec![VNode::VText(VText::new(self))]) + [VNode::VText(VText::new(self))].into_iter().collect() } } From 2401701e590a4066fb67775bde24f2a9a5cb5e59 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 18:14:32 +0100 Subject: [PATCH 06/28] Revert "Allow iterating over ref of ChildrenRenderer" This reverts commit 71bcd09af09de146aaf1d9adcec932eaab7b1088. --- packages/yew/src/html/component/children.rs | 72 +++++-------------- .../src/html/conversion/into_prop_value.rs | 33 ++++----- 2 files changed, 31 insertions(+), 74 deletions(-) diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index 63b44f008ba..701f00fd9df 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -3,10 +3,7 @@ use std::fmt; use std::rc::Rc; -use implicit_clone::{unsync::IArray, ImplicitClone}; - use crate::html::Html; -use crate::utils::RcExt; use crate::virtual_dom::{VChild, VComp, VList, VNode}; use crate::{BaseComponent, Properties}; @@ -155,13 +152,11 @@ pub type ChildrenWithProps = ChildrenRenderer>; /// A type used for rendering children html. #[derive(Clone)] -pub struct ChildrenRenderer { - pub(crate) children: IArray>, +pub struct ChildrenRenderer { + pub(crate) children: Vec, } -impl ImplicitClone for ChildrenRenderer {} - -impl PartialEq for ChildrenRenderer { +impl PartialEq for ChildrenRenderer { fn eq(&self, other: &Self) -> bool { self.children == other.children } @@ -169,13 +164,11 @@ impl PartialEq for ChildrenRenderer { impl ChildrenRenderer where - T: Clone + 'static, + T: Clone, { /// Create children pub fn new(children: Vec) -> Self { - Self { - children: children.into_iter().map(Rc::new).collect(), - } + Self { children } } /// Children list is empty @@ -192,8 +185,7 @@ where pub fn iter(&self) -> impl Iterator + '_ { // clone each child lazily. // This way `self.iter().next()` only has to clone a single node. - // TODO not sure if I shouldnt keep the Rc here - self.children.iter().map(RcExt::unwrap_or_clone) + self.children.iter().cloned() } /// Convert the children elements to another object (if there are any). @@ -205,7 +197,7 @@ where /// children.map(|children| { /// html! { ///
- /// {children} + /// {children.clone()} ///
/// } /// }) @@ -218,63 +210,37 @@ where closure(self) } } - - pub(crate) fn to_vec(&self) -> Vec { - self.iter().collect() - } } -impl Default for ChildrenRenderer { +impl Default for ChildrenRenderer { fn default() -> Self { Self { - children: Default::default(), + children: Vec::new(), } } } -impl fmt::Debug for ChildrenRenderer { +impl fmt::Debug for ChildrenRenderer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("ChildrenRenderer<_>") } } -#[derive(Debug)] -#[doc(hidden)] -pub struct Iter { - children: implicit_clone::unsync::Iter>, -} - -impl Iterator for Iter { - type Item = T; - - fn next(&mut self) -> Option { - self.children.next().map(|x| RcExt::unwrap_or_clone(x)) - } -} - -impl IntoIterator for ChildrenRenderer { - type IntoIter = Iter; +impl IntoIterator for ChildrenRenderer { + type IntoIter = std::vec::IntoIter; type Item = T; fn into_iter(self) -> Self::IntoIter { - Iter { - children: self.children.iter(), - } - } -} - -impl FromIterator for ChildrenRenderer { - fn from_iter>(it: IT) -> Self { - Self { - children: it.into_iter().map(Rc::new).collect(), - } + self.children.into_iter() } } impl From> for Html { - fn from(val: ChildrenRenderer) -> Self { + fn from(mut val: ChildrenRenderer) -> Self { if val.children.len() == 1 { - return RcExt::unwrap_or_clone(val.children[0].clone()); + if let Some(m) = val.children.pop() { + return m; + } } Html::VList(Rc::new(val.into())) @@ -286,7 +252,7 @@ impl From> for VList { if val.is_empty() { return VList::new(); } - VList::with_children(val.to_vec(), None) + VList::with_children(val.children, None) } } @@ -319,7 +285,7 @@ mod tests { #[test] fn children_map() { - let children = Children::new(Default::default()); + let children = Children::new(vec![]); let res = children.map(|children| Some(children.clone())); assert!(res.is_none()); let children = Children::new(vec![Default::default()]); diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 988ca79cc90..4b96129cd24 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -86,47 +86,47 @@ where impl IntoPropValue> for VChild where T: BaseComponent, - C: ImplicitClone + Into, + C: Clone + Into, VChild: Into, { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - [self.into()].into_iter().collect() + ChildrenRenderer::new(vec![self.into()]) } } impl IntoPropValue>> for VChild where T: BaseComponent, - C: ImplicitClone + Into, + C: Clone + Into, VChild: Into, { #[inline] fn into_prop_value(self) -> Option> { - Some([self.into()].into_iter().collect()) + Some(ChildrenRenderer::new(vec![self.into()])) } } impl IntoPropValue>> for Option> where T: BaseComponent, - C: ImplicitClone + Into, + C: Clone + Into, VChild: Into, { #[inline] fn into_prop_value(self) -> Option> { - self.map(|m| [m.into()].into_iter().collect()) + self.map(|m| ChildrenRenderer::new(vec![m.into()])) } } impl IntoPropValue> for Vec where T: Into, - R: ImplicitClone + Into, + R: Clone + Into, { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - self.into_iter().map(|m| m.into()).collect() + ChildrenRenderer::new(self.into_iter().map(|m| m.into()).collect()) } } @@ -167,33 +167,24 @@ impl IntoPropValue for ChildrenRenderer { } } -impl IntoPropValue for &ChildrenRenderer { - #[inline] - fn into_prop_value(self) -> VNode { - VNode::VList(Rc::new(self.clone().into())) - } -} - impl IntoPropValue> for VNode { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - [self].into_iter().collect() + ChildrenRenderer::new(vec![self]) } } impl IntoPropValue> for VText { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - [self.into()].into_iter().collect() + ChildrenRenderer::new(vec![self.into()]) } } impl IntoPropValue for ChildrenRenderer { #[inline] fn into_prop_value(self) -> VList { - // TODO converting IArray to Vec doesn't seem optimal, but it's too complicated to change - // VList for now - VList::with_children(self.to_vec(), None) + VList::with_children(self.children, None) } } @@ -206,7 +197,7 @@ impl IntoPropValue for VChild { impl IntoPropValue> for AttrValue { fn into_prop_value(self) -> ChildrenRenderer { - [VNode::VText(VText::new(self))].into_iter().collect() + ChildrenRenderer::new(vec![VNode::VText(VText::new(self))]) } } From 12aa26d245840b65e8709a50e0f0be383a522e62 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 18:17:44 +0100 Subject: [PATCH 07/28] Still allow ref on childrenrenderer but do not use IArray for now Too complicated --- packages/yew/src/html/component/children.rs | 2 +- packages/yew/src/html/conversion/into_prop_value.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index 701f00fd9df..8441348589c 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -197,7 +197,7 @@ where /// children.map(|children| { /// html! { ///
- /// {children.clone()} + /// {children} ///
/// } /// }) diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 4b96129cd24..3b5bbaead29 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -167,6 +167,13 @@ impl IntoPropValue for ChildrenRenderer { } } +impl IntoPropValue for &ChildrenRenderer { + #[inline] + fn into_prop_value(self) -> VNode { + VNode::VList(Rc::new(self.clone().into())) + } +} + impl IntoPropValue> for VNode { #[inline] fn into_prop_value(self) -> ChildrenRenderer { From 46e12dce7c7dda49cee093620a9961d9a7b74d78 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 1 Nov 2023 18:19:05 +0100 Subject: [PATCH 08/28] rustfmt nightly --- packages/yew/src/utils/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/yew/src/utils/mod.rs b/packages/yew/src/utils/mod.rs index 76b94df5cd8..743d266e986 100644 --- a/packages/yew/src/utils/mod.rs +++ b/packages/yew/src/utils/mod.rs @@ -1,9 +1,9 @@ //! This module contains useful utilities to get information about the current document. -use implicit_clone::unsync::IArray; -use implicit_clone::ImplicitClone; use std::marker::PhantomData; +use implicit_clone::unsync::IArray; +use implicit_clone::ImplicitClone; use yew::html::ChildrenRenderer; /// Map `IntoIterator>` to `Iterator` From 863f174fe93349bd3cf4132f2fe6f791d0d013bc Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 08:03:04 +0100 Subject: [PATCH 09/28] Revert changes on NodeSeq because I'm not sure it's useful at all --- packages/yew/src/utils/mod.rs | 52 ++++++++--------------------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/packages/yew/src/utils/mod.rs b/packages/yew/src/utils/mod.rs index 743d266e986..4f33ea672b2 100644 --- a/packages/yew/src/utils/mod.rs +++ b/packages/yew/src/utils/mod.rs @@ -2,8 +2,6 @@ use std::marker::PhantomData; -use implicit_clone::unsync::IArray; -use implicit_clone::ImplicitClone; use yew::html::ChildrenRenderer; /// Map `IntoIterator>` to `Iterator` @@ -17,64 +15,38 @@ where /// A special type necessary for flattening components returned from nested html macros. #[derive(Debug)] -pub struct NodeSeq(IArray, PhantomData); +pub struct NodeSeq(Vec, PhantomData); -impl, OUT: ImplicitClone + 'static> From for NodeSeq { +impl, OUT> From for NodeSeq { fn from(val: IN) -> Self { - Self(IArray::Single([val.into()]), PhantomData) + Self(vec![val.into()], PhantomData) } } -impl, OUT: ImplicitClone + 'static> From> for NodeSeq { +impl, OUT> From> for NodeSeq { fn from(val: Option) -> Self { - Self( - val.map(|s| IArray::Single([s.into()])).unwrap_or_default(), - PhantomData, - ) + Self(val.map(|s| vec![s.into()]).unwrap_or_default(), PhantomData) } } -impl, OUT: ImplicitClone + 'static> From> for NodeSeq { - fn from(mut val: Vec) -> Self { - if val.len() == 1 { - let item = val.pop().unwrap(); - Self(IArray::Single([item.into()]), PhantomData) - } else { - Self(val.into_iter().map(|x| x.into()).collect(), PhantomData) - } +impl, OUT> From> for NodeSeq { + fn from(val: Vec) -> Self { + Self(val.into_iter().map(|x| x.into()).collect(), PhantomData) } } -impl + ImplicitClone, OUT: ImplicitClone + 'static> From> - for NodeSeq -{ - fn from(val: IArray) -> Self { - Self(val.iter().map(|x| x.into()).collect(), PhantomData) - } -} - -impl + ImplicitClone, OUT: ImplicitClone + 'static> From<&IArray> - for NodeSeq -{ - fn from(val: &IArray) -> Self { - Self(val.iter().map(|x| x.into()).collect(), PhantomData) - } -} - -impl + Clone, OUT: ImplicitClone + 'static> From<&ChildrenRenderer> - for NodeSeq -{ +impl + Clone, OUT> From<&ChildrenRenderer> for NodeSeq { fn from(val: &ChildrenRenderer) -> Self { Self(val.iter().map(|x| x.into()).collect(), PhantomData) } } -impl IntoIterator for NodeSeq { - type IntoIter = implicit_clone::unsync::Iter; +impl IntoIterator for NodeSeq { + type IntoIter = std::vec::IntoIter; type Item = OUT; fn into_iter(self) -> Self::IntoIter { - self.0.iter() + self.0.into_iter() } } From 8242f53dac193302298febd1249d140af66bb7bc Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 08:04:14 +0100 Subject: [PATCH 10/28] Improve example, less clone() necessary --- examples/nested_list/src/list.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/nested_list/src/list.rs b/examples/nested_list/src/list.rs index d2639b6611a..5c8526f5d93 100644 --- a/examples/nested_list/src/list.rs +++ b/examples/nested_list/src/list.rs @@ -57,7 +57,7 @@ impl Component for List {
{ &ctx.props().header }
- { Self::view_items(ctx.props().children.clone()) } + { Self::view_items(&ctx.props().children) }
@@ -66,8 +66,9 @@ impl Component for List { } impl List { - fn view_items(children: IArray>) -> Html { + fn view_items(children: impl AsRef>>) -> Html { children + .as_ref() .iter() .filter(|c| !c.props.hide) .enumerate() From fc402b113bda1b7eddf88e5de9f335117b1fb3d9 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 08:10:37 +0100 Subject: [PATCH 11/28] Optimize VList creation from ref ChildrenRenderer --- packages/yew/src/html/conversion/into_prop_value.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 3b5bbaead29..6312a653f47 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -170,7 +170,10 @@ impl IntoPropValue for ChildrenRenderer { impl IntoPropValue for &ChildrenRenderer { #[inline] fn into_prop_value(self) -> VNode { - VNode::VList(Rc::new(self.clone().into())) + VNode::VList(Rc::new(VList::with_children( + self.children.iter().cloned().collect(), + None, + ))) } } From a9f9f937b645c0a67a3c944bc4cd7665cc46ef9b Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 08:13:03 +0100 Subject: [PATCH 12/28] Very unfortunate consequence of recent refactoring on implicit-clone --- .../tests/classes_macro/classes-fail.stderr | 12 +-- .../tests/html_macro/element-fail.stderr | 87 ++++++++++--------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/packages/yew-macro/tests/classes_macro/classes-fail.stderr b/packages/yew-macro/tests/classes_macro/classes-fail.stderr index 57cf0f21fb7..9d30a817b18 100644 --- a/packages/yew-macro/tests/classes_macro/classes-fail.stderr +++ b/packages/yew-macro/tests/classes_macro/classes-fail.stderr @@ -21,7 +21,7 @@ error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied >> > > - > + > >> >> > @@ -44,7 +44,7 @@ error[E0277]: the trait bound `Classes: From<{float}>` is not satisfied >> > > - > + > >> >> > @@ -67,7 +67,7 @@ error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied >> > > - > + > >> >> > @@ -93,7 +93,7 @@ error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied >> > > - > + > >> >> > @@ -119,7 +119,7 @@ error[E0277]: the trait bound `Classes: From` is not satisfied >> > > - > + > >> >> > @@ -145,7 +145,7 @@ error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied >> > > - > + > >> >> > diff --git a/packages/yew-macro/tests/html_macro/element-fail.stderr b/packages/yew-macro/tests/html_macro/element-fail.stderr index 11f9e7d631a..353e9e57218 100644 --- a/packages/yew-macro/tests/html_macro/element-fail.stderr +++ b/packages/yew-macro/tests/html_macro/element-fail.stderr @@ -396,76 +396,76 @@ note: function defined here | pub fn __ensure_type(_: T) {} | ^^^^^^^^^^^^^ -error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied +error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:43:26 | 43 | html! { }; - | ^^ the trait `IntoPropValue>` is not implemented for `()` + | ^^ the trait `IntoPropValue>` is not implemented for `()` | = help: the trait `IntoPropValue` is implemented for `()` -error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied +error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:44:27 | 44 | html! { }; - | ^^ the trait `IntoPropValue>` is not implemented for `()` + | ^^ the trait `IntoPropValue>` is not implemented for `()` | = help: the trait `IntoPropValue` is implemented for `()` -error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied +error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:45:22 | 45 | html! { }; - | ^^ the trait `IntoPropValue>` is not implemented for `()` + | ^^ the trait `IntoPropValue>` is not implemented for `()` | = help: the trait `IntoPropValue` is implemented for `()` -error[E0277]: the trait bound `NotToString: IntoPropValue>` is not satisfied +error[E0277]: the trait bound `NotToString: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:46:28 | 46 | html! { }; - | ^^^^^^^^^^^ the trait `IntoPropValue>` is not implemented for `NotToString` + | ^^^^^^^^^^^ the trait `IntoPropValue>` is not implemented for `NotToString` | = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> + <&'static [(K, V)] as IntoPropValue>> + <&'static [T] as IntoPropValue>> <&'static str as IntoPropValue> <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> + <&'static str as IntoPropValue>> <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&String as IntoPropValue> + <&'static str as IntoPropValue> + <&ChildrenRenderer as IntoPropValue> and $N others -error[E0277]: the trait bound `Option: IntoPropValue>` is not satisfied +error[E0277]: the trait bound `Option: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:47:23 | 47 | html! { }; - | ^^^^ the trait `IntoPropValue>` is not implemented for `Option` + | ^^^^ the trait `IntoPropValue>` is not implemented for `Option` | = help: the following other types implement trait `IntoPropValue`: as IntoPropValue>> - as IntoPropValue>> - > as IntoPropValue>> + as IntoPropValue>> + > as IntoPropValue>> as IntoPropValue>>> - > as IntoPropValue>> - as IntoPropValue>> + > as IntoPropValue>> + as IntoPropValue>> > as IntoPropValue>>> as IntoPropValue> -error[E0277]: the trait bound `Option<{integer}>: IntoPropValue>` is not satisfied +error[E0277]: the trait bound `Option<{integer}>: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:48:22 | 48 | html! { }; - | ^^^^ the trait `IntoPropValue>` is not implemented for `Option<{integer}>` + | ^^^^ the trait `IntoPropValue>` is not implemented for `Option<{integer}>` | = help: the following other types implement trait `IntoPropValue`: as IntoPropValue>> - as IntoPropValue>> - > as IntoPropValue>> + as IntoPropValue>> + > as IntoPropValue>> as IntoPropValue>>> - > as IntoPropValue>> - as IntoPropValue>> + > as IntoPropValue>> + as IntoPropValue>> > as IntoPropValue>>> as IntoPropValue> @@ -567,11 +567,11 @@ error[E0277]: the trait bound `Option: IntoPropValue | = help: the following other types implement trait `IntoPropValue`: as IntoPropValue>> - as IntoPropValue>> - > as IntoPropValue>> + as IntoPropValue>> + > as IntoPropValue>> as IntoPropValue>>> - > as IntoPropValue>> - as IntoPropValue>> + > as IntoPropValue>> + as IntoPropValue>> > as IntoPropValue>>> as IntoPropValue> = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -603,21 +603,21 @@ note: required by a bound in `yew::html::onclick::Wrapper::__macro_new` | |_^ required by this bound in `yew::html::onclick::Wrapper::__macro_new` = note: this error originates in the macro `impl_action` which comes from the expansion of the macro `impl_short` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `NotToString: IntoPropValue>` is not satisfied +error[E0277]: the trait bound `NotToString: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:60:28 | 60 | html! { }; - | ^^^^^^^^^^^ the trait `IntoPropValue>` is not implemented for `NotToString` + | ^^^^^^^^^^^ the trait `IntoPropValue>` is not implemented for `NotToString` | = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> + <&'static [(K, V)] as IntoPropValue>> + <&'static [T] as IntoPropValue>> <&'static str as IntoPropValue> <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> + <&'static str as IntoPropValue>> <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&String as IntoPropValue> + <&'static str as IntoPropValue> + <&ChildrenRenderer as IntoPropValue> and $N others error[E0277]: the trait bound `(): IntoPropValue` is not satisfied @@ -629,15 +629,16 @@ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied = help: the trait `IntoPropValue` is implemented for `()` = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `implicit_clone::unsync::IString: From<{integer}>` is not satisfied +error[E0277]: the trait bound `implicit_clone::unsync::string::IString: From<{integer}>` is not satisfied --> tests/html_macro/element-fail.rs:77:16 | 77 | html! { <@{55}> }; - | ^^ the trait `From<{integer}>` is not implemented for `implicit_clone::unsync::IString` + | ^^ the trait `From<{integer}>` is not implemented for `implicit_clone::unsync::string::IString` | = help: the following other types implement trait `From`: - > - >> - >> - > - = note: required because of the requirements on the impl of `Into` for `{integer}` + > + > + >> + >> + > + = note: required because of the requirements on the impl of `Into` for `{integer}` From 21f04efa6af0c5fcf1dd1f9d70cf8e8981dd9ded Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 08:18:16 +0100 Subject: [PATCH 13/28] The Vecs are actually of identical types... --- packages/yew/src/html/conversion/into_prop_value.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 6312a653f47..0eefdf15713 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -170,10 +170,7 @@ impl IntoPropValue for ChildrenRenderer { impl IntoPropValue for &ChildrenRenderer { #[inline] fn into_prop_value(self) -> VNode { - VNode::VList(Rc::new(VList::with_children( - self.children.iter().cloned().collect(), - None, - ))) + VNode::VList(Rc::new(VList::with_children(self.children.clone(), None))) } } From ba0b64ff77f780d7e41c195967bd1ccf4c6e6874 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 08:33:11 +0100 Subject: [PATCH 14/28] Use Rc> in ChildrenRenderer to avoid needless clones --- packages/yew/src/html/component/children.rs | 35 +++++++++++++------ .../src/html/conversion/into_prop_value.rs | 2 +- packages/yew/src/virtual_dom/vlist.rs | 4 +-- packages/yew/src/virtual_dom/vnode.rs | 2 +- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index 8441348589c..713875e1dc1 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -3,7 +3,8 @@ use std::fmt; use std::rc::Rc; -use crate::html::Html; +use crate::html::{Html, ImplicitClone}; +use crate::utils::RcExt; use crate::virtual_dom::{VChild, VComp, VList, VNode}; use crate::{BaseComponent, Properties}; @@ -151,11 +152,21 @@ pub type Children = ChildrenRenderer; pub type ChildrenWithProps = ChildrenRenderer>; /// A type used for rendering children html. -#[derive(Clone)] pub struct ChildrenRenderer { - pub(crate) children: Vec, + // TODO wrap in Option? + pub(crate) children: Rc>, } +impl Clone for ChildrenRenderer { + fn clone(&self) -> Self { + Self { + children: self.children.clone(), + } + } +} + +impl ImplicitClone for ChildrenRenderer {} + impl PartialEq for ChildrenRenderer { fn eq(&self, other: &Self) -> bool { self.children == other.children @@ -167,8 +178,10 @@ where T: Clone, { /// Create children - pub fn new(children: Vec) -> Self { - Self { children } + pub fn new(children: impl Into>>) -> Self { + Self { + children: children.into(), + } } /// Children list is empty @@ -215,7 +228,7 @@ where impl Default for ChildrenRenderer { fn default() -> Self { Self { - children: Vec::new(), + children: Default::default(), } } } @@ -226,19 +239,21 @@ impl fmt::Debug for ChildrenRenderer { } } -impl IntoIterator for ChildrenRenderer { +impl IntoIterator for ChildrenRenderer { type IntoIter = std::vec::IntoIter; type Item = T; fn into_iter(self) -> Self::IntoIter { - self.children.into_iter() + let children = RcExt::unwrap_or_clone(self.children); + children.into_iter() } } impl From> for Html { fn from(mut val: ChildrenRenderer) -> Self { if val.children.len() == 1 { - if let Some(m) = val.children.pop() { + let children = Rc::make_mut(&mut val.children); + if let Some(m) = children.pop() { return m; } } @@ -266,7 +281,7 @@ where .into_iter() .map(VComp::from) .map(VNode::from) - .collect(), + .collect::>(), ) } } diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 0eefdf15713..949094f362c 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -126,7 +126,7 @@ where { #[inline] fn into_prop_value(self) -> ChildrenRenderer { - ChildrenRenderer::new(self.into_iter().map(|m| m.into()).collect()) + ChildrenRenderer::new(self.into_iter().map(|m| m.into()).collect::>()) } } diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index 62dc5b14e0a..0d14a483cb2 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -72,10 +72,10 @@ impl VList { } /// Creates a new [VList] instance with children. - pub fn with_children(children: Vec, key: Option) -> Self { + pub fn with_children(children: impl Into>>, key: Option) -> Self { let mut vlist = VList { fully_keyed: FullyKeyedState::Unknown, - children: Some(Rc::new(children)), + children: Some(children.into()), key, }; vlist.recheck_fully_keyed(); diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs index 8ed18f29c9f..2579188d1f2 100644 --- a/packages/yew/src/virtual_dom/vnode.rs +++ b/packages/yew/src/virtual_dom/vnode.rs @@ -173,7 +173,7 @@ impl From for VNode { impl> FromIterator for VNode { fn from_iter>(iter: T) -> Self { VNode::VList(Rc::new(VList::with_children( - iter.into_iter().map(|n| n.into()).collect(), + iter.into_iter().map(|n| n.into()).collect::>(), None, ))) } From 3b0eac87a5bb76d4ada1e66b6cb2251251c38666 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 08:59:00 +0100 Subject: [PATCH 15/28] Avoid unnecessary allocation --- packages/yew/src/html/component/children.rs | 46 ++++++++++++------- .../src/html/conversion/into_prop_value.rs | 12 ++++- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index 713875e1dc1..a05d14e885b 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -153,8 +153,7 @@ pub type ChildrenWithProps = ChildrenRenderer>; /// A type used for rendering children html. pub struct ChildrenRenderer { - // TODO wrap in Option? - pub(crate) children: Rc>, + pub(crate) children: Option>>, } impl Clone for ChildrenRenderer { @@ -169,7 +168,12 @@ impl ImplicitClone for ChildrenRenderer {} impl PartialEq for ChildrenRenderer { fn eq(&self, other: &Self) -> bool { - self.children == other.children + match (self.children.as_ref(), other.children.as_ref()) { + (Some(a), Some(b)) => a == b, + (Some(a), None) => a.is_empty(), + (None, Some(b)) => b.is_empty(), + (None, None) => true, + } } } @@ -180,25 +184,25 @@ where /// Create children pub fn new(children: impl Into>>) -> Self { Self { - children: children.into(), + children: Some(children.into()), } } /// Children list is empty pub fn is_empty(&self) -> bool { - self.children.is_empty() + self.children.as_ref().map(|x| x.is_empty()).unwrap_or(true) } /// Number of children elements pub fn len(&self) -> usize { - self.children.len() + self.children.as_ref().map(|x| x.len()).unwrap_or(0) } /// Render children components and return `Iterator` pub fn iter(&self) -> impl Iterator + '_ { // clone each child lazily. // This way `self.iter().next()` only has to clone a single node. - self.children.iter().cloned() + self.children.iter().flat_map(|x| x.iter()).cloned() } /// Convert the children elements to another object (if there are any). @@ -244,17 +248,23 @@ impl IntoIterator for ChildrenRenderer { type Item = T; fn into_iter(self) -> Self::IntoIter { - let children = RcExt::unwrap_or_clone(self.children); - children.into_iter() + if let Some(children) = self.children { + let children = RcExt::unwrap_or_clone(children); + children.into_iter() + } else { + Vec::new().into_iter() + } } } impl From> for Html { fn from(mut val: ChildrenRenderer) -> Self { - if val.children.len() == 1 { - let children = Rc::make_mut(&mut val.children); - if let Some(m) = children.pop() { - return m; + if let Some(children) = val.children.as_mut() { + if children.len() == 1 { + let children = Rc::make_mut(children); + if let Some(m) = children.pop() { + return m; + } } } @@ -264,10 +274,14 @@ impl From> for Html { impl From> for VList { fn from(val: ChildrenRenderer) -> Self { - if val.is_empty() { - return VList::new(); + if let Some(children) = val.children { + if children.is_empty() { + return VList::new(); + } + VList::with_children(children, None) + } else { + VList::new() } - VList::with_children(val.children, None) } } diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 949094f362c..f7f653396be 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -170,7 +170,11 @@ impl IntoPropValue for ChildrenRenderer { impl IntoPropValue for &ChildrenRenderer { #[inline] fn into_prop_value(self) -> VNode { - VNode::VList(Rc::new(VList::with_children(self.children.clone(), None))) + if let Some(children) = self.children.clone() { + VNode::VList(Rc::new(VList::with_children(children, None))) + } else { + VNode::VList(Rc::new(VList::new())) + } } } @@ -191,7 +195,11 @@ impl IntoPropValue> for VText { impl IntoPropValue for ChildrenRenderer { #[inline] fn into_prop_value(self) -> VList { - VList::with_children(self.children, None) + if let Some(children) = self.children { + VList::with_children(children, None) + } else { + VList::new() + } } } From 13676be92cc6f177ae796959ec0f686c59c276c3 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 09:01:36 +0100 Subject: [PATCH 16/28] Fix invalid use of as_ref() --- examples/nested_list/src/list.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/nested_list/src/list.rs b/examples/nested_list/src/list.rs index 5c8526f5d93..c73c071d25e 100644 --- a/examples/nested_list/src/list.rs +++ b/examples/nested_list/src/list.rs @@ -66,9 +66,8 @@ impl Component for List { } impl List { - fn view_items(children: impl AsRef>>) -> Html { + fn view_items(children: &IArray>) -> Html { children - .as_ref() .iter() .filter(|c| !c.props.hide) .enumerate() From 47e965189e76a3df4360f555a19b86616150940f Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 09:01:57 +0100 Subject: [PATCH 17/28] Oh actually it was useful This reverts commit 863f174fe93349bd3cf4132f2fe6f791d0d013bc. --- packages/yew/src/utils/mod.rs | 52 +++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/packages/yew/src/utils/mod.rs b/packages/yew/src/utils/mod.rs index 4f33ea672b2..743d266e986 100644 --- a/packages/yew/src/utils/mod.rs +++ b/packages/yew/src/utils/mod.rs @@ -2,6 +2,8 @@ use std::marker::PhantomData; +use implicit_clone::unsync::IArray; +use implicit_clone::ImplicitClone; use yew::html::ChildrenRenderer; /// Map `IntoIterator>` to `Iterator` @@ -15,38 +17,64 @@ where /// A special type necessary for flattening components returned from nested html macros. #[derive(Debug)] -pub struct NodeSeq(Vec, PhantomData); +pub struct NodeSeq(IArray, PhantomData); -impl, OUT> From for NodeSeq { +impl, OUT: ImplicitClone + 'static> From for NodeSeq { fn from(val: IN) -> Self { - Self(vec![val.into()], PhantomData) + Self(IArray::Single([val.into()]), PhantomData) } } -impl, OUT> From> for NodeSeq { +impl, OUT: ImplicitClone + 'static> From> for NodeSeq { fn from(val: Option) -> Self { - Self(val.map(|s| vec![s.into()]).unwrap_or_default(), PhantomData) + Self( + val.map(|s| IArray::Single([s.into()])).unwrap_or_default(), + PhantomData, + ) } } -impl, OUT> From> for NodeSeq { - fn from(val: Vec) -> Self { - Self(val.into_iter().map(|x| x.into()).collect(), PhantomData) +impl, OUT: ImplicitClone + 'static> From> for NodeSeq { + fn from(mut val: Vec) -> Self { + if val.len() == 1 { + let item = val.pop().unwrap(); + Self(IArray::Single([item.into()]), PhantomData) + } else { + Self(val.into_iter().map(|x| x.into()).collect(), PhantomData) + } } } -impl + Clone, OUT> From<&ChildrenRenderer> for NodeSeq { +impl + ImplicitClone, OUT: ImplicitClone + 'static> From> + for NodeSeq +{ + fn from(val: IArray) -> Self { + Self(val.iter().map(|x| x.into()).collect(), PhantomData) + } +} + +impl + ImplicitClone, OUT: ImplicitClone + 'static> From<&IArray> + for NodeSeq +{ + fn from(val: &IArray) -> Self { + Self(val.iter().map(|x| x.into()).collect(), PhantomData) + } +} + +impl + Clone, OUT: ImplicitClone + 'static> From<&ChildrenRenderer> + for NodeSeq +{ fn from(val: &ChildrenRenderer) -> Self { Self(val.iter().map(|x| x.into()).collect(), PhantomData) } } -impl IntoIterator for NodeSeq { - type IntoIter = std::vec::IntoIter; +impl IntoIterator for NodeSeq { + type IntoIter = implicit_clone::unsync::Iter; type Item = OUT; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + self.0.iter() } } From a80d1d42b6edccad416b73a16efb3746c49ecbce Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 09:25:27 +0100 Subject: [PATCH 18/28] Fix: NodeSeq now requires OUT to implement ImplicitClone --- packages/yew-macro/tests/html_macro/component-pass.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/yew-macro/tests/html_macro/component-pass.rs b/packages/yew-macro/tests/html_macro/component-pass.rs index 5cc59c4208d..b4a38a3bbae 100644 --- a/packages/yew-macro/tests/html_macro/component-pass.rs +++ b/packages/yew-macro/tests/html_macro/component-pass.rs @@ -67,6 +67,8 @@ pub enum ChildrenVariants { AltChild(::yew::virtual_dom::VChild), } +impl ::yew::html::ImplicitClone for ChildrenVariants {} + impl ::std::convert::From<::yew::virtual_dom::VChild> for ChildrenVariants { fn from(comp: ::yew::virtual_dom::VChild) -> Self { ChildrenVariants::Child(comp) From ab1e13877d1ef9988a203b1631ce4156660c80ed Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 09:57:56 +0100 Subject: [PATCH 19/28] Revert unnecessary change --- packages/yew/src/html/component/children.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index a05d14e885b..c428217c116 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -182,9 +182,13 @@ where T: Clone, { /// Create children - pub fn new(children: impl Into>>) -> Self { - Self { - children: Some(children.into()), + pub fn new(children: Vec) -> Self { + if children.is_empty() { + Self { children: None } + } else { + Self { + children: Some(Rc::new(children)), + } } } From 38d45d97b5af5974410ef51a93ac5881dfc51761 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 10:26:13 +0100 Subject: [PATCH 20/28] Simplify code, restore original with_children() signature, optimize --- packages/yew/src/html/component/children.rs | 9 +-- .../src/html/conversion/into_prop_value.rs | 16 ++---- packages/yew/src/virtual_dom/vlist.rs | 55 ++++++++++++++++--- packages/yew/src/virtual_dom/vnode.rs | 8 +-- 4 files changed, 55 insertions(+), 33 deletions(-) diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index c428217c116..94205309bb5 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -278,14 +278,7 @@ impl From> for Html { impl From> for VList { fn from(val: ChildrenRenderer) -> Self { - if let Some(children) = val.children { - if children.is_empty() { - return VList::new(); - } - VList::with_children(children, None) - } else { - VList::new() - } + VList::from(val.children) } } diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index f7f653396be..3a75f6b42d2 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -170,11 +170,7 @@ impl IntoPropValue for ChildrenRenderer { impl IntoPropValue for &ChildrenRenderer { #[inline] fn into_prop_value(self) -> VNode { - if let Some(children) = self.children.clone() { - VNode::VList(Rc::new(VList::with_children(children, None))) - } else { - VNode::VList(Rc::new(VList::new())) - } + VNode::VList(Rc::new(VList::from(self.children.clone()))) } } @@ -195,18 +191,14 @@ impl IntoPropValue> for VText { impl IntoPropValue for ChildrenRenderer { #[inline] fn into_prop_value(self) -> VList { - if let Some(children) = self.children { - VList::with_children(children, None) - } else { - VList::new() - } + VList::from(self.children) } } impl IntoPropValue for VChild { #[inline] fn into_prop_value(self) -> VList { - VList::with_children(vec![self.into()], None) + VList::from_iter([VNode::from(self)].into_iter()) } } @@ -219,7 +211,7 @@ impl IntoPropValue> for AttrValue { impl IntoPropValue for Vec { #[inline] fn into_prop_value(self) -> VNode { - VNode::VList(Rc::new(VList::with_children(self, None))) + VNode::VList(Rc::new(VList::from_iter(self))) } } diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index 0d14a483cb2..bd366204eb4 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -61,6 +61,37 @@ impl DerefMut for VList { } } +impl> FromIterator for VList { + fn from_iter>(iter: T) -> Self { + let children = iter.into_iter().map(|n| n.into()).collect::>(); + if children.is_empty() { + VList::new() + } else { + VList { + children: Some(Rc::new(children)), + key: None, + fully_keyed: FullyKeyedState::KnownFullyKeyed, + } + } + } +} + +impl From>>> for VList { + fn from(children: Option>>) -> Self { + if children.as_ref().map(|x| x.is_empty()).unwrap_or(true) { + VList::new() + } else { + let mut vlist = VList { + fully_keyed: FullyKeyedState::Unknown, + children, + key: None, + }; + vlist.recheck_fully_keyed(); + vlist + } + } +} + impl VList { /// Creates a new empty [VList] instance. pub const fn new() -> Self { @@ -72,14 +103,22 @@ impl VList { } /// Creates a new [VList] instance with children. - pub fn with_children(children: impl Into>>, key: Option) -> Self { - let mut vlist = VList { - fully_keyed: FullyKeyedState::Unknown, - children: Some(children.into()), - key, - }; - vlist.recheck_fully_keyed(); - vlist + pub fn with_children(children: Vec, key: Option) -> Self { + if children.is_empty() { + VList { + fully_keyed: FullyKeyedState::KnownFullyKeyed, + children: None, + key, + } + } else { + let mut vlist = VList { + fully_keyed: FullyKeyedState::Unknown, + children: Some(Rc::new(children)), + key, + }; + vlist.recheck_fully_keyed(); + vlist + } } // Returns a mutable reference to children, allocates the children if it hasn't been done. diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs index 2579188d1f2..8ae86098284 100644 --- a/packages/yew/src/virtual_dom/vnode.rs +++ b/packages/yew/src/virtual_dom/vnode.rs @@ -65,8 +65,7 @@ impl VNode { match *self { Self::VList(ref mut m) => return Rc::make_mut(m), _ => { - *self = - VNode::VList(Rc::new(VList::with_children(vec![mem::take(self)], None))); + *self = VNode::VList(Rc::new(VList::from_iter([mem::take(self)].into_iter()))); } } } @@ -172,9 +171,8 @@ impl From for VNode { impl> FromIterator for VNode { fn from_iter>(iter: T) -> Self { - VNode::VList(Rc::new(VList::with_children( - iter.into_iter().map(|n| n.into()).collect::>(), - None, + VNode::VList(Rc::new(VList::from_iter( + iter.into_iter().map(|n| n.into()), ))) } } From aa788c35c7aff17314221f1fb9afadfee7e406ee Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 10:29:40 +0100 Subject: [PATCH 21/28] clippy --- packages/yew/src/html/conversion/into_prop_value.rs | 2 +- packages/yew/src/virtual_dom/vnode.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 3a75f6b42d2..3b9b3666432 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -198,7 +198,7 @@ impl IntoPropValue for ChildrenRenderer { impl IntoPropValue for VChild { #[inline] fn into_prop_value(self) -> VList { - VList::from_iter([VNode::from(self)].into_iter()) + VList::from_iter([VNode::from(self)]) } } diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs index 8ae86098284..ff8e5dd9600 100644 --- a/packages/yew/src/virtual_dom/vnode.rs +++ b/packages/yew/src/virtual_dom/vnode.rs @@ -65,7 +65,7 @@ impl VNode { match *self { Self::VList(ref mut m) => return Rc::make_mut(m), _ => { - *self = VNode::VList(Rc::new(VList::from_iter([mem::take(self)].into_iter()))); + *self = VNode::VList(Rc::new(VList::from_iter([mem::take(self)]))); } } } From bc96cc70c7dae0d3fbb44d994cab20f51ddeeda2 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 10:31:06 +0100 Subject: [PATCH 22/28] oops --- packages/yew/src/virtual_dom/vlist.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index bd366204eb4..e861e41d817 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -69,8 +69,8 @@ impl> FromIterator for VList { } else { VList { children: Some(Rc::new(children)), + fully_keyed: FullyKeyedState::Unknown, key: None, - fully_keyed: FullyKeyedState::KnownFullyKeyed, } } } @@ -82,8 +82,8 @@ impl From>>> for VList { VList::new() } else { let mut vlist = VList { - fully_keyed: FullyKeyedState::Unknown, children, + fully_keyed: FullyKeyedState::Unknown, key: None, }; vlist.recheck_fully_keyed(); From 0a9a3bc1d9437c163ee08e026bbf878e4e0b11cf Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 10:42:35 +0100 Subject: [PATCH 23/28] Less allocations probably --- .../src/html/conversion/into_prop_value.rs | 4 +-- packages/yew/src/virtual_dom/vlist.rs | 28 +++++++++++++++++++ packages/yew/src/virtual_dom/vnode.rs | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 3b9b3666432..608268eec67 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -198,7 +198,7 @@ impl IntoPropValue for ChildrenRenderer { impl IntoPropValue for VChild { #[inline] fn into_prop_value(self) -> VList { - VList::from_iter([VNode::from(self)]) + VList::from(VNode::from(self)) } } @@ -211,7 +211,7 @@ impl IntoPropValue> for AttrValue { impl IntoPropValue for Vec { #[inline] fn into_prop_value(self) -> VNode { - VNode::VList(Rc::new(VList::from_iter(self))) + VNode::VList(Rc::new(VList::from(self))) } } diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index e861e41d817..70198429df5 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -92,6 +92,34 @@ impl From>>> for VList { } } +impl From> for VList { + fn from(children: Vec) -> Self { + if children.is_empty() { + VList::new() + } else { + let mut vlist = VList { + children: Some(Rc::new(children)), + fully_keyed: FullyKeyedState::Unknown, + key: None, + }; + vlist.recheck_fully_keyed(); + vlist + } + } +} + +impl From for VList { + fn from(child: VNode) -> Self { + let mut vlist = VList { + children: Some(Rc::new(vec![child])), + fully_keyed: FullyKeyedState::Unknown, + key: None, + }; + vlist.recheck_fully_keyed(); + vlist + } +} + impl VList { /// Creates a new empty [VList] instance. pub const fn new() -> Self { diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs index ff8e5dd9600..8accae27119 100644 --- a/packages/yew/src/virtual_dom/vnode.rs +++ b/packages/yew/src/virtual_dom/vnode.rs @@ -65,7 +65,7 @@ impl VNode { match *self { Self::VList(ref mut m) => return Rc::make_mut(m), _ => { - *self = VNode::VList(Rc::new(VList::from_iter([mem::take(self)]))); + *self = VNode::VList(Rc::new(VList::from(mem::take(self)))); } } } From d7e4acf8a69db73eb18a90dd1a49021e3e93eb44 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 10:51:58 +0100 Subject: [PATCH 24/28] Code duplication --- packages/yew/src/virtual_dom/vlist.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index 70198429df5..32094f78b1a 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -132,21 +132,9 @@ impl VList { /// Creates a new [VList] instance with children. pub fn with_children(children: Vec, key: Option) -> Self { - if children.is_empty() { - VList { - fully_keyed: FullyKeyedState::KnownFullyKeyed, - children: None, - key, - } - } else { - let mut vlist = VList { - fully_keyed: FullyKeyedState::Unknown, - children: Some(Rc::new(children)), - key, - }; - vlist.recheck_fully_keyed(); - vlist - } + let mut vlist = VList::from(children); + vlist.key = key; + vlist } // Returns a mutable reference to children, allocates the children if it hasn't been done. From 25f34948ded5bb878c1e386c41147e95969e8b4a Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Mon, 6 Nov 2023 11:49:29 +0100 Subject: [PATCH 25/28] Trigger CI From 64c4b63d04a76a7cd0c820178912fc70bfa9ec98 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 11 Nov 2023 09:36:00 +0100 Subject: [PATCH 26/28] Upgrade implicit-clone to 0.4.7 and use derive macro when possible --- Cargo.lock | 15 ++++++++++++-- examples/nested_list/src/main.rs | 4 +--- .../tests/html_macro/component-pass.rs | 4 +--- packages/yew/Cargo.toml | 2 +- packages/yew/src/html/classes.rs | 6 ++---- .../src/html/conversion/into_prop_value.rs | 3 +-- packages/yew/src/html/mod.rs | 2 +- packages/yew/src/virtual_dom/key.rs | 4 +--- packages/yew/src/virtual_dom/listeners.rs | 20 ++----------------- packages/yew/src/virtual_dom/vlist.rs | 4 +--- packages/yew/src/virtual_dom/vnode.rs | 4 +--- packages/yew/src/virtual_dom/vraw.rs | 4 +--- packages/yew/src/virtual_dom/vsuspense.rs | 4 +--- packages/yew/src/virtual_dom/vtag.rs | 12 +++-------- packages/yew/src/virtual_dom/vtext.rs | 4 +--- 15 files changed, 31 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 747ec22c3df..9a0fccf9aac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1675,13 +1675,24 @@ dependencies = [ [[package]] name = "implicit-clone" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "789a53d49d2276908b1fcccb9dfcf65c7a6e884c1df639f26aeff4f142b3ef9e" +checksum = "16c5448a864f9abf124ef8bf2a3cc37eb9fd99fe2e804a8b3235d7357bca2c25" dependencies = [ + "implicit-clone-derive", "indexmap 2.0.2", ] +[[package]] +name = "implicit-clone-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9311685eb9a34808bbb0608ad2fcab9ae216266beca5848613e95553ac914e3b" +dependencies = [ + "quote", + "syn 2.0.38", +] + [[package]] name = "indexmap" version = "1.9.3" diff --git a/examples/nested_list/src/main.rs b/examples/nested_list/src/main.rs index 480b680a3b7..510b941941c 100644 --- a/examples/nested_list/src/main.rs +++ b/examples/nested_list/src/main.rs @@ -40,7 +40,7 @@ impl PartialEq for WeakComponentLink { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, ImplicitClone, PartialEq, Eq, Hash)] pub enum Hovered { Header, Item(AttrValue), @@ -48,8 +48,6 @@ pub enum Hovered { None, } -impl ImplicitClone for Hovered {} - impl fmt::Display for Hovered { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( diff --git a/packages/yew-macro/tests/html_macro/component-pass.rs b/packages/yew-macro/tests/html_macro/component-pass.rs index b4a38a3bbae..2cbf800eb53 100644 --- a/packages/yew-macro/tests/html_macro/component-pass.rs +++ b/packages/yew-macro/tests/html_macro/component-pass.rs @@ -61,14 +61,12 @@ impl ::yew::Component for Container { } } -#[derive(::std::clone::Clone, ::std::cmp::PartialEq)] +#[derive(::std::clone::Clone, ::yew::html::ImplicitClone, ::std::cmp::PartialEq)] pub enum ChildrenVariants { Child(::yew::virtual_dom::VChild), AltChild(::yew::virtual_dom::VChild), } -impl ::yew::html::ImplicitClone for ChildrenVariants {} - impl ::std::convert::From<::yew::virtual_dom::VChild> for ChildrenVariants { fn from(comp: ::yew::virtual_dom::VChild) -> Self { ChildrenVariants::Child(comp) diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index e896e34c6b7..321c8d3db59 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -27,7 +27,7 @@ yew-macro = { version = "^0.21.0", path = "../yew-macro" } thiserror = "1.0" futures = { version = "0.3", default-features = false, features = ["std"] } html-escape = { version = "0.2.13", optional = true } -implicit-clone = { version = "0.4.5", features = ["map"] } +implicit-clone = { version = "0.4.7", features = ["map"] } base64ct = { version = "1.6.0", features = ["std"], optional = true } bincode = { version = "1.3.3", optional = true } serde = { version = "1", features = ["derive"] } diff --git a/packages/yew/src/html/classes.rs b/packages/yew/src/html/classes.rs index 941ec617bf2..607e51331a7 100644 --- a/packages/yew/src/html/classes.rs +++ b/packages/yew/src/html/classes.rs @@ -2,23 +2,21 @@ use std::borrow::Cow; use std::iter::FromIterator; use std::rc::Rc; -use implicit_clone::ImplicitClone; use indexmap::IndexSet; use super::IntoPropValue; +use crate::html::ImplicitClone; use crate::utils::RcExt; use crate::virtual_dom::AttrValue; /// A set of classes, cheap to clone. /// /// The preferred way of creating this is using the [`classes!`][yew::classes!] macro. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, ImplicitClone, Default)] pub struct Classes { set: Rc>, } -impl ImplicitClone for Classes {} - /// helper method to efficiently turn a set of classes into a space-separated /// string. Abstracts differences between ToString and IntoPropValue. The /// `rest` iterator is cloned to pre-compute the length of the String; it diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 608268eec67..1ec505c9aed 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -6,10 +6,9 @@ use implicit_clone::unsync::{IArray, IMap}; pub use implicit_clone::ImplicitClone; use crate::callback::Callback; -use crate::html::{BaseComponent, ChildrenRenderer, Component, NodeRef, Scope}; +use crate::html::{BaseComponent, ChildrenRenderer, Component, Scope}; use crate::virtual_dom::{AttrValue, VChild, VList, VNode, VText}; -impl ImplicitClone for NodeRef {} impl ImplicitClone for Scope {} // TODO there are still a few missing diff --git a/packages/yew/src/html/mod.rs b/packages/yew/src/html/mod.rs index cbb0b1c5488..e31d081010f 100644 --- a/packages/yew/src/html/mod.rs +++ b/packages/yew/src/html/mod.rs @@ -87,7 +87,7 @@ impl IntoHtmlResult for Html { /// ``` /// ## Relevant examples /// - [Node Refs](https://github.com/yewstack/yew/tree/master/examples/node_refs) -#[derive(Default, Clone)] +#[derive(Default, Clone, ImplicitClone)] pub struct NodeRef(Rc>); impl PartialEq for NodeRef { diff --git a/packages/yew/src/virtual_dom/key.rs b/packages/yew/src/virtual_dom/key.rs index 8ef29abc528..cdadd287b90 100644 --- a/packages/yew/src/virtual_dom/key.rs +++ b/packages/yew/src/virtual_dom/key.rs @@ -9,7 +9,7 @@ use crate::html::ImplicitClone; /// Represents the (optional) key of Yew's virtual nodes. /// /// Keys are cheap to clone. -#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[derive(Clone, ImplicitClone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] pub struct Key { key: Rc, } @@ -41,8 +41,6 @@ impl From<&'_ str> for Key { } } -impl ImplicitClone for Key {} - macro_rules! key_impl_from_to_string { ($type:ty) => { impl From<$type> for Key { diff --git a/packages/yew/src/virtual_dom/listeners.rs b/packages/yew/src/virtual_dom/listeners.rs index f96dead2f39..7eeb93d52ea 100644 --- a/packages/yew/src/virtual_dom/listeners.rs +++ b/packages/yew/src/virtual_dom/listeners.rs @@ -160,18 +160,17 @@ gen_listener_kinds! { } /// A list of event listeners -#[derive(Debug)] +#[derive(Debug, Clone, ImplicitClone, Default)] pub enum Listeners { /// No listeners registered or pending. /// Distinct from `Pending` with an empty slice to avoid an allocation. + #[default] None, /// Not yet added to the element or registry Pending(Box<[Option>]>), } -impl ImplicitClone for Listeners {} - impl PartialEq for Listeners { fn eq(&self, rhs: &Self) -> bool { use Listeners::*; @@ -201,18 +200,3 @@ impl PartialEq for Listeners { } } } - -impl Clone for Listeners { - fn clone(&self) -> Self { - match self { - Self::None => Self::None, - Self::Pending(v) => Self::Pending(v.clone()), - } - } -} - -impl Default for Listeners { - fn default() -> Self { - Self::None - } -} diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index 32094f78b1a..ccdd38006d3 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use super::{Key, VNode}; use crate::html::ImplicitClone; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, ImplicitClone, Copy, Debug, PartialEq)] enum FullyKeyedState { KnownFullyKeyed, KnownMissingKeys, @@ -24,8 +24,6 @@ pub struct VList { pub key: Option, } -impl ImplicitClone for VList {} - impl PartialEq for VList { fn eq(&self, other: &Self) -> bool { self.key == other.key && self.children == other.children diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs index 8accae27119..a29f3a1248c 100644 --- a/packages/yew/src/virtual_dom/vnode.rs +++ b/packages/yew/src/virtual_dom/vnode.rs @@ -13,7 +13,7 @@ use crate::virtual_dom::VRaw; use crate::AttrValue; /// Bind virtual element to a DOM reference. -#[derive(Clone, PartialEq)] +#[derive(Clone, ImplicitClone, PartialEq)] #[must_use = "html does not do anything unless returned to Yew for rendering."] pub enum VNode { /// A bind between `VTag` and `Element`. @@ -36,8 +36,6 @@ pub enum VNode { VRaw(VRaw), } -impl ImplicitClone for VNode {} - impl VNode { pub fn key(&self) -> Option<&Key> { match self { diff --git a/packages/yew/src/virtual_dom/vraw.rs b/packages/yew/src/virtual_dom/vraw.rs index 9c8e7a3c46a..82bcc110063 100644 --- a/packages/yew/src/virtual_dom/vraw.rs +++ b/packages/yew/src/virtual_dom/vraw.rs @@ -2,13 +2,11 @@ use crate::html::ImplicitClone; use crate::AttrValue; /// A raw HTML string to be used in VDOM. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, ImplicitClone, Debug, PartialEq, Eq)] pub struct VRaw { pub html: AttrValue, } -impl ImplicitClone for VRaw {} - impl From for VRaw { fn from(html: AttrValue) -> Self { Self { html } diff --git a/packages/yew/src/virtual_dom/vsuspense.rs b/packages/yew/src/virtual_dom/vsuspense.rs index 8167f4fb2e1..39539c93f25 100644 --- a/packages/yew/src/virtual_dom/vsuspense.rs +++ b/packages/yew/src/virtual_dom/vsuspense.rs @@ -2,7 +2,7 @@ use super::{Key, VNode}; use crate::html::ImplicitClone; /// This struct represents a suspendable DOM fragment. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, ImplicitClone, Debug, PartialEq)] pub struct VSuspense { /// Child nodes. pub(crate) children: VNode, @@ -14,8 +14,6 @@ pub struct VSuspense { pub(crate) key: Option, } -impl ImplicitClone for VSuspense {} - impl VSuspense { pub fn new(children: VNode, fallback: VNode, suspended: bool, key: Option) -> Self { Self { diff --git a/packages/yew/src/virtual_dom/vtag.rs b/packages/yew/src/virtual_dom/vtag.rs index 0b0d9699049..796ea49904f 100644 --- a/packages/yew/src/virtual_dom/vtag.rs +++ b/packages/yew/src/virtual_dom/vtag.rs @@ -63,7 +63,7 @@ impl Deref for Value { /// Fields specific to /// [InputElement](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) [VTag](crate::virtual_dom::VTag)s -#[derive(Debug, Clone, Default, Eq, PartialEq)] +#[derive(Debug, Clone, ImplicitClone, Default, Eq, PartialEq)] pub(crate) struct InputFields { /// Contains a value of an /// [InputElement](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). @@ -76,8 +76,6 @@ pub(crate) struct InputFields { pub(crate) checked: Option, } -impl ImplicitClone for InputFields {} - impl Deref for InputFields { type Target = Value; @@ -104,7 +102,7 @@ impl InputFields { /// [VTag] fields that are specific to different [VTag] kinds. /// Decreases the memory footprint of [VTag] by avoiding impossible field and value combinations. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, ImplicitClone)] pub(crate) enum VTagInner { /// Fields specific to /// [InputElement](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) @@ -127,12 +125,10 @@ pub(crate) enum VTagInner { }, } -impl ImplicitClone for VTagInner {} - /// A type for a virtual /// [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) /// representation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, ImplicitClone)] pub struct VTag { /// [VTag] fields that are specific to different [VTag] kinds. pub(crate) inner: VTagInner, @@ -145,8 +141,6 @@ pub struct VTag { pub key: Option, } -impl ImplicitClone for VTag {} - impl VTag { /// Creates a new [VTag] instance with `tag` name (cannot be changed later in DOM). pub fn new(tag: impl Into) -> Self { diff --git a/packages/yew/src/virtual_dom/vtext.rs b/packages/yew/src/virtual_dom/vtext.rs index c1a3d5f38c0..7d6c02e4e82 100644 --- a/packages/yew/src/virtual_dom/vtext.rs +++ b/packages/yew/src/virtual_dom/vtext.rs @@ -8,14 +8,12 @@ use crate::html::ImplicitClone; /// A type for a virtual /// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode) /// representation. -#[derive(Clone)] +#[derive(Clone, ImplicitClone)] pub struct VText { /// Contains a text of the node. pub text: AttrValue, } -impl ImplicitClone for VText {} - impl VText { /// Creates new virtual text node with a content. pub fn new(text: impl Into) -> Self { From b9baa2908cc1cb993608053ed5d08236c2335582 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 11 Nov 2023 12:33:48 +0100 Subject: [PATCH 27/28] Fix bad location for symbol in test --- Cargo.lock | 1 + packages/yew-macro/Cargo.toml | 1 + packages/yew-macro/tests/html_macro/component-pass.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 9a0fccf9aac..c8e622e9dfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3892,6 +3892,7 @@ dependencies = [ name = "yew-macro" version = "0.21.0" dependencies = [ + "implicit-clone", "once_cell", "prettyplease", "proc-macro-error", diff --git a/packages/yew-macro/Cargo.toml b/packages/yew-macro/Cargo.toml index 9d497486b92..57d24fd7e4b 100644 --- a/packages/yew-macro/Cargo.toml +++ b/packages/yew-macro/Cargo.toml @@ -28,3 +28,4 @@ prettyplease = "0.2" rustversion = "1" trybuild = "1" yew = { path = "../yew" } +implicit-clone = "0.4" diff --git a/packages/yew-macro/tests/html_macro/component-pass.rs b/packages/yew-macro/tests/html_macro/component-pass.rs index 2cbf800eb53..81e8f05c448 100644 --- a/packages/yew-macro/tests/html_macro/component-pass.rs +++ b/packages/yew-macro/tests/html_macro/component-pass.rs @@ -61,7 +61,7 @@ impl ::yew::Component for Container { } } -#[derive(::std::clone::Clone, ::yew::html::ImplicitClone, ::std::cmp::PartialEq)] +#[derive(::std::clone::Clone, ::implicit_clone::ImplicitClone, ::std::cmp::PartialEq)] pub enum ChildrenVariants { Child(::yew::virtual_dom::VChild), AltChild(::yew::virtual_dom::VChild), From 224e08b6538a601a0978d88811aebb60f18125ea Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 19 Nov 2023 11:06:59 +0100 Subject: [PATCH 28/28] Update implicit-clone to 0.4.8 --- Cargo.lock | 4 ++-- packages/yew/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8e622e9dfd..ed191b17fe5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1675,9 +1675,9 @@ dependencies = [ [[package]] name = "implicit-clone" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c5448a864f9abf124ef8bf2a3cc37eb9fd99fe2e804a8b3235d7357bca2c25" +checksum = "fc06a255cbf402a52ae399c05a518c68c569c916ea11423620111df576b9b9bb" dependencies = [ "implicit-clone-derive", "indexmap 2.0.2", diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index 321c8d3db59..27c6c545ef8 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -27,7 +27,7 @@ yew-macro = { version = "^0.21.0", path = "../yew-macro" } thiserror = "1.0" futures = { version = "0.3", default-features = false, features = ["std"] } html-escape = { version = "0.2.13", optional = true } -implicit-clone = { version = "0.4.7", features = ["map"] } +implicit-clone = { version = "0.4.8", features = ["map"] } base64ct = { version = "1.6.0", features = ["std"], optional = true } bincode = { version = "1.3.3", optional = true } serde = { version = "1", features = ["derive"] }