Skip to content

Tracking Issue for #![feature(downcast_unchecked)] #90850

Open

Description

@ibraheemdev

View all comments

Feature gate: #![feature(downcast_unchecked)]

This is a tracking issue for the downcast_unchecked, downcast_ref_unchecked, and downcast_mut_unchecked methods.

Public API

impl dyn Any (+ Send + Sync) {
    pub unsafe fn downcast_ref_unchecked<T: Any>(&self) -> &T;
    pub unsafe fn downcast_mut_unchecked<T: Any>(&mut self) -> &mut T;
}

impl<A: Allocator> Box<dyn Any (+ Send + Sync), A> {
    pub unsafe fn downcast_unchecked<T: Any>(&self) -> Box<T, A>;
}

Steps / History

Unresolved Questions

  • None yet.

Activity

  1. added
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    T-libs-api[DEPRECATED; DO NOT USE]
    on Nov 13, 2021
  2. david-perez commented on Jan 10, 2022

    @david-perez
    Contributor

    Should these be added to std::error::Error too?

  3. ibraheemdev commented on Jan 11, 2022

    @ibraheemdev
    MemberAuthor

    I don't see how that would be useful. You generally don't know the concrete type of an error before downcasting.

  4. lunabunn commented on May 1, 2022

    @lunabunn

    This would allow me to shed my dependency on unsafe_any. What's the state of this feature? Is it ready for an FCP?

  5. ibraheemdev commented on May 1, 2022

    @ibraheemdev
    MemberAuthor

    Now that {Option, Result}::unwrap_unchecked are stabilized, I wonder if there is much benefit to these methods.

  6. added a commit that references this issue on Jun 20, 2022
  7. CAD97 commented on Oct 6, 2022

    @CAD97
    Contributor

    Instead of downcast_unchecked, is there a good reason to not just have the more general

    impl Box<T: ?Sized, A> {
        pub unsafe fn cast_unchecked<U>(self) -> Box<U, A> {
            debug_assert_eq!(Layout::for_value(&*self), Layout::new::<U>());
            unsafe {
                let (raw, alloc) = Box::into_raw_with_allocator(self);
                Box::from_raw_in(raw.cast(), alloc)
            }
        }
    }

    ? I suppose you lose the debug_assert! for correct typing which is possible if you have dyn Any, though it is true that you can already any.downcast().unwrap_unchecked() for that.

    To immediately answer my own question: Box doesn't want to add methods to arbitrary types like that. cast_unchecked is still useful but would probably instead be pub unsafe fn Box::<T: ?Sized, A>::cast<U>(this: Self) -> Box<U, A> to make it an associated function rather than a method which could shadow a pointee's method.

  8. yshui commented on Nov 13, 2022

    @yshui
    Contributor

    @ibraheemdev this still generates better code than downcast_ref().unwrap_unchecked().

    pub fn cast(a: &dyn std::any::Any) -> &i32 {
        unsafe { a.downcast_ref_unchecked() }
    }

    compiles to

    example::cast:
            mov     rax, rdi
            ret

    whereas

    pub fn cast(a: &dyn std::any::Any) -> &i32 {
        unsafe { a.downcast_ref().unwrap_unchecked() }
    }

    becomes

    example::xx:
            push    rbx
            mov     rbx, rdi
            call    qword ptr [rsi + 24]
            mov     rax, rbx
            pop     rbx
            ret

    Both compiled with opt-level=3

  9. yshui commented on Nov 13, 2022

    @yshui
    Contributor

    I think llvm didn't realize that T::type_id call is pure (because it's called through the vtable?), so it kept the call.

  10. yshui commented on Nov 13, 2022

    @yshui
    Contributor

    huh, why is type_id not stored directly in the vtable?

  11. CAD97 commented on Nov 14, 2022

    @CAD97
    Contributor

    huh, why is type_id not stored directly in the vtable?

    The short version is that Rust doesn't have a way to get associated constant data from a trait object (yet).

  12. gh2o commented on Dec 9, 2022

    @gh2o
    Contributor

    The semantics of a.downcast_ref_unchecked() and a.downcast_ref().unwrap_unchecked() are still different. For example, with a.downcast_ref_unchecked() one could "transmute" between two types that are different but #[repr(C)] compatible in layout, whereas with a.downcast_ref().unwrap_unchecked() you'd likely be dereferencing a null pointer.

  13. marc0246 commented on May 5, 2023

    @marc0246
    Contributor

    I'd like to point out that in all other places in the std, the ref/mut part comes after the unchecked part. Therefore this should be downcast_unchecked_ref and downcast_unchecked_mut to stay concistent AFAICS.

  14. Wasabi375 commented on Jun 26, 2024

    @Wasabi375

    Hey, is there a reason that Box::downcast and similar functions require T to be sized? AFAICT they should work with unsized types.

  15. 6 remaining items

  16. taj-p commented on Oct 20, 2025

    @taj-p
    Contributor

    Sorry, I'm new to Rust contribution. Does this mean that if we rename to downcast_unchecked_ref and downcast_unchecked_mut we can consider this API ready for stabilization @marc0246 ?

    I'm happy to open a PR with that change.

  17. marc0246 commented on Oct 23, 2025

    @marc0246
    Contributor

    @taj-p if you have the time to make the PR, I would very much appreciate it. You can link my comment above for the justification. But note that I have no decision-making power here. You could make a stabilization PR right now, although I would raise the same concern and hopefully the powers that be would take it into consideration. At least, that's how the governance is supposed to work.

  18. added a commit that references this issue on Nov 9, 2025
  19. added a commit that references this issue on Nov 9, 2025
  20. added a commit that references this issue on Nov 30, 2025
  21. Mysvac commented on Dec 16, 2025

    @Mysvac

    Has this API not been stabilized yet? It would be very useful in reflection-related libraries:

    use std::any::Any;
    
    trait Reflect: Any {}
    
    impl dyn Reflect {
        fn downcast<T: Reflect>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> {
            // `<Box<dyn Any>>::downcast` returns `Result<Box<T>, Box<dyn Any>>`
            // Unable to convert `Box<dyn Any>` back to `Box<dyn Reflect>`,
            // therefore it is necessary to check the type in advance.
            if self.is::<T>() {
                unsafe {
                    // // `TypeId` will be checked again here?
                    // Ok(<Box<dyn Any>>::downcast(self).unwrap_unchecked())
                    
                    // Good, `TypeId` will not be checked again.
                    Ok(<Box<dyn Any>>::downcast_unchecked(self))
                }
            } else {
                Err(self)
            }
        }
    }

    Although compiler optimizations might eliminate the inner check in downcast after inlining (since the two checks are identical), it's still beneficial to explicitly remove one redundant check.

  22. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions