Repository: linebender/xilem
Pull request #929: Streamline Widget trait using trait upcasting
Author: login: PoignardAzur
Created: 2025-04-17T12:06:29Z | Merged: 2025-04-25T15:08:59Z

Bump MSRV to 1.86


Diff:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2cb87b5300..1c2b8b0b23 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,7 +8,7 @@ env:
   # If the compilation fails, then the version specified here needs to be bumped up to reality.
   # Be sure to also update the rust-version property in the workspace Cargo.toml file,
   # plus all the README.md files of the affected packages.
-  RUST_MIN_VER: "1.85"
+  RUST_MIN_VER: "1.86"
   # List of packages that can not target Wasm.
   NO_WASM_PKGS: "--exclude masonry --exclude masonry_core --exclude xilem"
   # Only some of our examples support Android (primarily due to extra required boilerplate).
diff --git a/Cargo.toml b/Cargo.toml
index 385692dcf2..bbe5558cea 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,7 @@ members = [
 [workspace.package]
 edition = "2024"
 # Keep in sync with RUST_MIN_VER in .github/workflows/ci.yml, with the relevant README.md files.
-rust-version = "1.85"
+rust-version = "1.86"
 license = "Apache-2.0"
 repository = "https://github.com/linebender/xilem"
 homepage = "https://xilem.dev/"
diff --git a/README.md b/README.md
index a5c9cd9590..07925c7bd0 100644
--- a/README.md
+++ b/README.md
@@ -137,7 +137,7 @@ sudo apt-get install clang libwayland-dev libxkbcommon-x11-dev libvulkan-dev
 
 ## Minimum supported Rust Version (MSRV)
 
-This version of Xilem has been verified to compile with **Rust 1.85** and later.
+This version of Xilem has been verified to compile with **Rust 1.86** and later.
 
 Future versions of Xilem might increase the Rust version requirement.
 It will not be treated as a breaking change and as such can even happen with small patch releases.
diff --git a/masonry/README.md b/masonry/README.md
index a4152aafae..c369f3a1a7 100644
--- a/masonry/README.md
+++ b/masonry/README.md
@@ -124,7 +124,7 @@ Masonry apps currently ship with two debugging features built in:
 
 ## Minimum supported Rust Version (MSRV)
 
-This version of Masonry has been verified to compile with **Rust 1.85** and later.
+This version of Masonry has been verified to compile with **Rust 1.86** and later.
 
 Future versions of Masonry might increase the Rust version requirement.
 It will not be treated as a breaking change and as such can even happen with small patch releases.
diff --git a/masonry_core/src/core/contexts.rs b/masonry_core/src/core/contexts.rs
index b30ca55ee9..c504eafce2 100644
--- a/masonry_core/src/core/contexts.rs
+++ b/masonry_core/src/core/contexts.rs
@@ -3,6 +3,8 @@
 
 //! The context types that are passed into various widget methods.
 
+use std::any::Any;
+
 use accesskit::TreeUpdate;
 use anymap3::AnyMap;
 use dpi::LogicalPosition;
@@ -158,7 +160,9 @@ impl_context_method!(
                 .widget_children
                 .item(child.id())
                 .expect("get_child: child not found");
-            child_ref.item.as_dyn_any().downcast_ref::<Child>().unwrap()
+            (child_ref.item as &dyn Any)
+                .downcast_ref::<Child>()
+                .unwrap()
         }
 
         #[allow(dead_code, reason = "Copy-pasted for some types that don't need it")]
@@ -168,7 +172,7 @@ impl_context_method!(
                 .widget_children
                 .item(child.id())
                 .expect("get_child: child not found");
-            child_ref.item.as_dyn()
+            &**child_ref.item
         }
 
         #[allow(dead_code, reason = "Copy-pasted for some types that don't need it")]
diff --git a/masonry_core/src/core/widget.rs b/masonry_core/src/core/widget.rs
index 33fc05426e..af280855c5 100644
--- a/masonry_core/src/core/widget.rs
+++ b/masonry_core/src/core/widget.rs
@@ -13,7 +13,6 @@ use tracing::field::DisplayValue;
 use tracing::{Span, trace_span};
 use vello::Scene;
 
-use crate::AsAny;
 use crate::core::{
     AccessCtx, AccessEvent, BoxConstraints, ComposeCtx, EventCtx, LayoutCtx, PaintCtx,
     PointerEvent, PropertiesMut, PropertiesRef, QueryCtx, RegisterCtx, TextEvent, Update,
@@ -82,11 +81,11 @@ pub trait FromDynWidget {
 
 impl<T: Widget> FromDynWidget for T {
     fn from_dyn(widget: &dyn Widget) -> Option<&Self> {
-        widget.as_any().downcast_ref()
+        (widget as &dyn Any).downcast_ref()
     }
 
     fn from_dyn_mut(widget: &mut dyn Widget) -> Option<&mut Self> {
-        widget.as_mut_any().downcast_mut()
+        (widget as &mut dyn Any).downcast_mut()
     }
 }
 
@@ -125,7 +124,7 @@ impl FromDynWidget for dyn Widget {
 /// through [`WidgetPod`](crate::core::WidgetPod)s. Widget methods are called by Masonry, and a
 /// widget should only be mutated either during a method call or through a [`WidgetMut`](crate::core::WidgetMut).
 #[allow(unused_variables)]
-pub trait Widget: AsAny + AsDynWidget {
+pub trait Widget: AsDynWidget + Any {
     /// Handle a pointer event.
     ///
     /// Pointer events will target the widget under the pointer, and then the
@@ -389,24 +388,6 @@ pub trait Widget: AsAny + AsDynWidget {
             .last()
             .unwrap_or(name)
     }
-
-    // FIXME
-    /// Cast as `Any`.
-    ///
-    /// Mainly intended to be overridden in `Box<dyn Widget>`.
-    #[doc(hidden)]
-    fn as_any(&self) -> &dyn Any {
-        self.as_dyn_any()
-    }
-
-    // FIXME
-    /// Cast as `Any`.
-    ///
-    /// Mainly intended to be overridden in `Box<dyn Widget>`.
-    #[doc(hidden)]
-    fn as_mut_any(&mut self) -> &mut dyn Any {
-        self.as_mut_dyn_any()
-    }
 }
 
 /// See [`Widget::find_widget_at_pos`] for more details.
diff --git a/masonry_core/src/core/widget_ref.rs b/masonry_core/src/core/widget_ref.rs
index b80f07e2e8..1f238e6f25 100644
--- a/masonry_core/src/core/widget_ref.rs
+++ b/masonry_core/src/core/widget_ref.rs
@@ -1,6 +1,7 @@
 // Copyright 2018 the Xilem Authors and the Druid Authors
 // SPDX-License-Identifier: Apache-2.0
 
+use std::any::Any;
 use std::ops::Deref;
 
 use smallvec::SmallVec;
@@ -101,7 +102,7 @@ impl<'w, W: Widget + ?Sized> WidgetRef<'w, W> {
         Some(WidgetRef {
             ctx: self.ctx,
             properties: self.properties,
-            widget: self.widget.as_any().downcast_ref()?,
+            widget: (self.widget.as_dyn() as &dyn Any).downcast_ref()?,
         })
     }
 
diff --git a/masonry_core/src/lib.rs b/masonry_core/src/lib.rs
index a6af516992..1ceb0f0911 100644
--- a/masonry_core/src/lib.rs
+++ b/masonry_core/src/lib.rs
@@ -63,4 +63,4 @@ pub use vello::{kurbo, peniko};
 pub use {cursor_icon, dpi, parley, vello};
 
 // TODO - Move to core?
-pub use util::{AsAny, Handled, UnitPoint};
+pub use util::{Handled, UnitPoint};
diff --git a/masonry_core/src/testing/helper_widgets.rs b/masonry_core/src/testing/helper_widgets.rs
index ddac827a6f..2df8b33f05 100644
--- a/masonry_core/src/testing/helper_widgets.rs
+++ b/masonry_core/src/testing/helper_widgets.rs
@@ -18,7 +18,6 @@ use smallvec::SmallVec;
 use tracing::trace_span;
 use vello::Scene;
 
-use crate::AsAny;
 use crate::core::{
     AccessCtx, AccessEvent, BoxConstraints, ComposeCtx, EventCtx, LayoutCtx, PaintCtx,
     PointerEvent, PropertiesMut, PropertiesRef, QueryCtx, RegisterCtx, TextEvent, Update,
@@ -477,14 +476,6 @@ impl<S: 'static> Widget for ModularWidget<S> {
     fn short_type_name(&self) -> &'static str {
         "ModularWidget"
     }
-
-    fn as_any(&self) -> &dyn std::any::Any {
-        self.as_dyn_any()
-    }
-
-    fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
-        self.as_mut_dyn_any()
-    }
 }
 
 impl ReplaceChild {
@@ -734,12 +725,4 @@ impl<W: Widget> Widget for Recorder<W> {
     fn short_type_name(&self) -> &'static str {
         "Recorder"
     }
-
-    fn as_any(&self) -> &dyn std::any::Any {
-        self.child.as_any()
-    }
-
-    fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
-        self.child.as_mut_any()
-    }
 }
diff --git a/masonry_core/src/util.rs b/masonry_core/src/util.rs
index 194dd1e35f..3c5e9854bd 100644
--- a/masonry_core/src/util.rs
+++ b/masonry_core/src/util.rs
@@ -3,7 +3,6 @@
 
 //! Miscellaneous utility functions.
 
-use std::any::Any;
 use std::hash::Hash;
 
 use vello::Scene;
@@ -61,28 +60,6 @@ impl From<bool> for Handled {
     }
 }
 
-// ---
-
-/// Trait extending Any, implemented for all types that implement Any.
-///
-/// This is a band-aid to substitute for a lack of dyn trait upcasting.
-pub trait AsAny: Any {
-    /// Return self.
-    fn as_dyn_any(&self) -> &dyn Any;
-    /// Return self.
-    fn as_mut_dyn_any(&mut self) -> &mut dyn Any;
-}
-
-impl<T: Any> AsAny for T {
-    fn as_dyn_any(&self) -> &dyn Any {
-        self
-    }
-
-    fn as_mut_dyn_any(&mut self) -> &mut dyn Any {
-        self
-    }
-}
-
 // --- MARK: PAINT HELPERS ---
 
 #[derive(Debug, Clone, Copy)]
diff --git a/xilem/README.md b/xilem/README.md
index 6b5d984584..1dbd9fda7c 100644
--- a/xilem/README.md
+++ b/xilem/README.md
@@ -150,7 +150,7 @@ You should also expect to use the adapters from Xilem Core, including:
 
 ## Minimum supported Rust Version (MSRV)
 
-This version of Xilem has been verified to compile with **Rust 1.85** and later.
+This version of Xilem has been verified to compile with **Rust 1.86** and later.
 
 Future versions of Xilem might increase the Rust version requirement.
 It will not be treated as a breaking change and as such can even happen with small patch releases.
diff --git a/xilem_core/README.md b/xilem_core/README.md
index d8d8e54839..c10da97306 100644
--- a/xilem_core/README.md
+++ b/xilem_core/README.md
@@ -47,7 +47,7 @@ If you wish to use Xilem Core in environments where an allocator is not availabl
 
 ## Minimum supported Rust Version (MSRV)
 
-This version of Xilem Core has been verified to compile with **Rust 1.85** and later.
+This version of Xilem Core has been verified to compile with **Rust 1.86** and later.
 
 Future versions of Xilem Core might increase the Rust version requirement.
 It will not be treated as a breaking change and as such can even happen with small patch releases.
diff --git a/xilem_web/README.md b/xilem_web/README.md
index bca8ad598e..e28303d20e 100644
--- a/xilem_web/README.md
+++ b/xilem_web/README.md
@@ -52,7 +52,7 @@ pub fn main() {
 
 ## Minimum supported Rust Version (MSRV)
 
-This version of Xilem Web has been verified to compile with **Rust 1.85** and later.
+This version of Xilem Web has been verified to compile with **Rust 1.86** and later.
 
 Future versions of Xilem Web might increase the Rust version requirement.
 It will not be treated as a breaking change and as such can even happen with small patch releases.


Reviews:
user: login: DJMcNab
body: Seems reasonable to me
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-17T12:49:26Z
user: login: PoignardAzur
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T13:08:18Z

Review comments:
diff_hunk: @@ -734,12 +725,4 @@ impl<W: Widget> Widget for Recorder<W> {
     fn short_type_name(&self) -> &'static str {
         "Recorder"
     }
-
-    fn as_any(&self) -> &dyn std::any::Any {
-        self.child.as_any()
path: masonry_core/src/testing/helper_widgets.rs
user: login: DJMcNab
body: This seems to be different semantics, right?
created_at: 2025-04-17T12:49:05Z
author_association: MEMBER
diff_hunk: @@ -734,12 +725,4 @@ impl<W: Widget> Widget for Recorder<W> {
     fn short_type_name(&self) -> &'static str {
         "Recorder"
     }
-
-    fn as_any(&self) -> &dyn std::any::Any {
-        self.child.as_any()
path: masonry_core/src/testing/helper_widgets.rs
user: login: PoignardAzur
body: True, it makes downcasting code "skip" the Recorder wrapper.

These downcasting tricks became mostly redundant since we removed `impl Widget for Box<dyn Widget>`, and they were a source of headaches before then.
created_at: 2025-04-17T13:08:18Z
author_association: CONTRIBUTOR

PR comments:
user: login: PoignardAzur
created_at: 2025-04-17T12:24:19Z
body: Not sure where the `cargo doc` ICE is coming from. It's not a fluke, I was triggered again when I re-ran the job, but running the same command on my machine yields no error.

Ideally I'd like to send a bug report, but I have no idea how to get the `rustc-ice-<...>.txt` file.

I'm hoping someone else will report the bug and we can merge this in a few weeks.
author_association: CONTRIBUTOR
user: login: DJMcNab
created_at: 2025-04-17T12:50:19Z
body: The way to get the file is to add uploading it as an artifact (or `cat`ting it) as a step in CI.
author_association: MEMBER
user: login: DJMcNab
created_at: 2025-04-17T16:30:02Z
body: It's strange that you can't reproduce it even on the same nightly version that CI uses though.
author_association: MEMBER