From fe8f9b8431ca382b36f27fd92352fc7d677300b4 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Fri, 19 Jan 2024 21:22:40 +0100 Subject: [PATCH] Require `View: ViewMarker` to avoid the leaky abstraction `fn view() -> impl View + ViewMarker` Motivation: to avoid requiring `fn view() -> impl View + ViewMarker` and instead just using `fn view() -> impl View` I don't see a reason for types that implement `View`, but not `ViewMarker`. This requirement has the neat side-effect of avoiding weird error-messages otherwise, in case `impl ViewMarker for T` has been forgotten for `impl View for T`. The whole purpose AFAIK of `ViewMarker` is a workaround of the orphan rule, that something like this: ```rust impl + ViewMarker> ViewSequence for V ``` is possible. So I tried adding this as super trait requirement and it seems to be possible. I think this makes the leaky abstraction/workaround much more feasible as it's only required now for actual `View` implementations. --- crates/xilem_core/src/view/mod.rs | 12 +++++++----- crates/xilem_web/src/interfaces.rs | 4 ++-- crates/xilem_web/src/view.rs | 2 +- src/view/view.rs | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/xilem_core/src/view/mod.rs b/crates/xilem_core/src/view/mod.rs index 3fc4837e7..645ad2bd1 100644 --- a/crates/xilem_core/src/view/mod.rs +++ b/crates/xilem_core/src/view/mod.rs @@ -9,16 +9,18 @@ mod memoize; /// Arguments are /// /// - `$viewtrait` - The name of the view trait we want to generate. +/// - `$viewmarker` - The name of the viewmarker trait as a workaround for Rust's orphan rules, +/// to allow views to be also viewsequences. /// - `$bound` - A bound on all element types that will be used. /// - `$cx` - The name of text context type that will be passed to the `build`/`rebuild` /// methods, and be responsible for managing element creation & deletion. /// - `$changeflags` - The type that reports down/up the tree. Can be used to avoid /// doing work when we can prove nothing needs doing. -/// - `$ss` - (optional) parent traits to this trait (e.g. `:Send`). Also applied to -/// the state type requirements +/// - `$super_bounds` - (optional) parent traits to this trait (e.g. `+ Send`). +/// - `$state_bounds` - (optional) trait bounds for the associated type `State` (e.g. `: Send`). #[macro_export] macro_rules! generate_view_trait { - ($viewtrait:ident, $bound:ident, $cx:ty, $changeflags:ty; $($ss:tt)*) => { + ($viewtrait:ident, $viewmarker:ident, $bound:ident, $cx:ty, $changeflags:ty; ($($super_bounds:tt)*), ($($state_bounds:tt)*)) => { /// A view object representing a node in the UI. /// /// This is a central trait for representing UI. An app will generate a tree of @@ -36,9 +38,9 @@ macro_rules! generate_view_trait { /// and also a type for actions which are passed up the tree in message /// propagation. During message handling, mutable access to the app state is /// given to view nodes, which in turn can expose it to callbacks. - pub trait $viewtrait $( $ss )* { + pub trait $viewtrait: $viewmarker $( $super_bounds )* { /// Associated state for the view. - type State $( $ss )*; + type State $( $state_bounds )*; /// The associated element for the view. type Element: $bound; diff --git a/crates/xilem_web/src/interfaces.rs b/crates/xilem_web/src/interfaces.rs index 56a37038a..202ef7f90 100644 --- a/crates/xilem_web/src/interfaces.rs +++ b/crates/xilem_web/src/interfaces.rs @@ -1,4 +1,4 @@ -use crate::{Pointer, PointerMsg, View, ViewMarker}; +use crate::{Pointer, PointerMsg, View}; use std::borrow::Cow; use gloo::events::EventListenerOptions; @@ -29,7 +29,7 @@ macro_rules! event_handler_mixin { }; } -pub trait Element: View + ViewMarker + sealed::Sealed +pub trait Element: View + sealed::Sealed where Self: Sized, { diff --git a/crates/xilem_web/src/view.rs b/crates/xilem_web/src/view.rs index b9d79003f..edaf2e0a1 100644 --- a/crates/xilem_web/src/view.rs +++ b/crates/xilem_web/src/view.rs @@ -84,7 +84,7 @@ impl Pod { } } -xilem_core::generate_view_trait! {View, DomNode, Cx, ChangeFlags;} +xilem_core::generate_view_trait! {View, ViewMarker, DomNode, Cx, ChangeFlags;(),()} xilem_core::generate_viewsequence_trait! {ViewSequence, View, ViewMarker, DomNode, Cx, ChangeFlags, Pod;} xilem_core::generate_anyview_trait! {AnyView, View, ViewMarker, Cx, ChangeFlags, AnyNode, BoxedView;} xilem_core::generate_memoize_view! {Memoize, MemoizeState, View, ViewMarker, Cx, ChangeFlags, static_view, memoize;} diff --git a/src/view/view.rs b/src/view/view.rs index 3a1bfacac..30ec59a01 100644 --- a/src/view/view.rs +++ b/src/view/view.rs @@ -23,7 +23,7 @@ use xilem_core::{Id, IdPath}; use crate::widget::{AnyWidget, ChangeFlags, Pod, Widget}; -xilem_core::generate_view_trait! {View, Widget, Cx, ChangeFlags; : Send} +xilem_core::generate_view_trait! {View, ViewMarker, Widget, Cx, ChangeFlags; (+ Send), (: Send)} xilem_core::generate_viewsequence_trait! {ViewSequence, View, ViewMarker, Widget, Cx, ChangeFlags, Pod; : Send} xilem_core::generate_anyview_trait! {AnyView, View, ViewMarker, Cx, ChangeFlags, AnyWidget, BoxedView; + Send} xilem_core::generate_memoize_view! {Memoize, MemoizeState, View, ViewMarker, Cx, ChangeFlags, s, memoize; + Send}