Skip to content

Add as_inner helper method on trait objects#4160

Merged
kchibisov merged 9 commits intomasterfrom
madsmtm/as-inner
Mar 11, 2025
Merged

Add as_inner helper method on trait objects#4160
kchibisov merged 9 commits intomasterfrom
madsmtm/as-inner

Conversation

@madsmtm
Copy link
Copy Markdown
Member

@madsmtm madsmtm commented Mar 9, 2025

Avoids issues with calling .as_any().downcast_ref() on smart pointers, and makes it simpler to convert into the backend type, and clearer which kind of conversion we support. Alternative to #4157.

We should also be able to completely remove AsAny once we have MSRV 1.86, which is why I've added Any as a supertrait of that.

It's actually a miracle that crate::monitor::MonitorHandle -> dyn crate::monitor::MonitorHandleProvider -> .as_any -> platform_impl::MonitorHandle path that we use currently works at all (which it only does because all method calls do an implicit deref).

  • Tested on all platforms changed
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality
    • Not yet, backend types aren't yet exposed.

@madsmtm madsmtm added S - enhancement Wouldn't this be the coolest? S - api Design and usability labels Mar 9, 2025
Avoids issues with calling `.as_any().downcast_ref()` on smart pointers.
@madsmtm madsmtm force-pushed the madsmtm/as-inner branch from 98652e5 to 354891d Compare March 9, 2025 23:02
Copy link
Copy Markdown
Member

@kchibisov kchibisov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove the impl <T: Any> stuff from utils?

@kchibisov
Copy link
Copy Markdown
Member

You'd also need a mutable counter-part, and I'm also not sure how casts that take ownership would work. E.g. when you cast something inside the Box you can get an owned type.

I'd also name it as get_as and get_as_mut.

@madsmtm
Copy link
Copy Markdown
Member Author

madsmtm commented Mar 10, 2025

Could we remove the impl <T: Any> stuff from utils?

Not yet, but AsAny is private, and can now continue being so (so the pitfalls from the T: Any impl aren't as perilous).

You'd also need a mutable counter-part, and I'm also not sure how casts that take ownership would work. E.g. when you cast something inside the Box you can get an owned type.

Added as_inner_mut and into_inner in second and fourth commit.

I'd also name it as get_as and get_as_mut.

I chose as_inner to be more closely aligned with the C-CONV Rust API guideline.

@madsmtm madsmtm requested a review from kchibisov March 10, 2025 09:55
@kchibisov
Copy link
Copy Markdown
Member

I chose as_inner to be more closely aligned with the C-CONV Rust API guideline.

There's nothing inner though, you just try to get by doing a cast, hence get. Usually such methods are named cast. Inner just isn't it.

@kchibisov
Copy link
Copy Markdown
Member

Not yet, but AsAny is private, and can now continue being so (so the pitfalls from the T: Any impl aren't as perilous).

The point was that it's easy to mess things up with it, thus I want it removed. Not as any, but the T: Any impl.

@madsmtm
Copy link
Copy Markdown
Member Author

madsmtm commented Mar 10, 2025

I chose as_inner to be more closely aligned with the C-CONV Rust API guideline.

There's nothing inner though, you just try to get by doing a cast, hence get. Usually such methods are named cast. Inner just isn't it.

I'd probably be fine with cast, cast_mut and cast_into. Or cast_ref, cast_mut and cast?

Not yet, but AsAny is private, and can now continue being so (so the pitfalls from the T: Any impl aren't as perilous).

The point was that it's easy to mess things up with it, thus I want it removed. Not as any, but the T: Any impl.

But the problematic part isn't actually the as_any, nor the T: Any impl; the problematic part is the combination of .as_any().downcast_ref::<X>().unwrap(). And I believe that when using .as_inner::<X>().unwrap(), those problems are no more.

If you want, we can rename the as_any/... methods to something like __as_any to signify that they should be avoided?

@kchibisov
Copy link
Copy Markdown
Member

I mean, my solution solved all of that, method was present only where it was needed and expected.

@madsmtm
Copy link
Copy Markdown
Member Author

madsmtm commented Mar 10, 2025

I mean, my solution solved all of that, method was present only where it was needed and expected.

Yeah, but it also introduced an extra API that all backends have to implement, which feels ugly IMO.

Besides, regardless of resolving the problem at hand, I believe the methods as_inner/... I propose here will be valuable conveniences to users (i.e. I will want them even if we go with #4157).

@kchibisov
Copy link
Copy Markdown
Member

Sure. The reason why I've brought the API is to ensure that the casts actually cast to the right thing, despite of how we write them and e.g. you won't be tempted to use as_any because it's present for the user.

Like I was debugging an issue where my cast wasn't working during implementing an API, thus I wanted some mechanism in place to prevent issues like that to ever appear. Yes, it results in a bit more code to get things more type-safe, because of rust's implicit Deref.

They can be difficult to use correctly.
@madsmtm
Copy link
Copy Markdown
Member Author

madsmtm commented Mar 10, 2025

Like I was debugging an issue where my cast wasn't working during implementing an API, thus I wanted some mechanism in place to prevent issues like that to ever appear. Yes, it results in a bit more code to get things more type-safe, because of rust's implicit Deref.

I fully agree that this is a big issue, and that we should resolve it!

I've renamed the AsAny trait methods to __as_any/__as_any_mut/__into_any, and marked them #[doc(hidden)], that way, they shouldn't show up even to rust-analyzer (and again, in the future, AsAny will be completely gone).


My biggest problem with T: OpaqueObject is that it won't really help once rust-lang/rust#65991 is usable by us, since at that point you can convert any &T -> &dyn Any anyhow.

Copy link
Copy Markdown
Member

@kchibisov kchibisov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to cast_ref, cast_mut, and cast. Since we logically upcast -> downcast, hence cast.

@madsmtm
Copy link
Copy Markdown
Member Author

madsmtm commented Mar 10, 2025

Rename to cast_ref, cast_mut, and cast. Since we logically upcast -> downcast, hence cast.

Done.

I think I slightly prefer cast/cast_mut/cast_into, since I think &dyn Window -> &T is going to be the most common operation, but I also like cast_ref/cast_mut/cast, since it mirrors the std API.

@kchibisov
Copy link
Copy Markdown
Member

@madsmtm could you check if the latest commit is fine? I just don't like that we'd have to duplicate the actual code for pretty much every opaque object we add into the winit-core, thus it'll add required cast methods.

We could probably use a macro_rules! though instead to add a proper bound for such calls as alternative.

@kchibisov
Copy link
Copy Markdown
Member

Added one more commit that restricts the casting, but uses macro for that. I think bounding by the trait in cast is a good idea, and probably I'd rather go with it.

Copy link
Copy Markdown
Member Author

@madsmtm madsmtm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I like the macro, pushed a commit to fix some docs, but otherwise excellent!

@kchibisov kchibisov merged commit 16d5f46 into master Mar 11, 2025
57 checks passed
@kchibisov kchibisov deleted the madsmtm/as-inner branch March 11, 2025 13:35
@madsmtm madsmtm mentioned this pull request May 15, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S - api Design and usability S - enhancement Wouldn't this be the coolest?

Development

Successfully merging this pull request may close these issues.

2 participants