Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions library/core/src/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,18 +1007,23 @@ pub const fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
#[must_use]
#[unstable(feature = "try_as_dyn", issue = "144361")]
pub const fn try_as_dyn<
T: Any + 'static,
T: Any + ?Sized + 'static,
U: ptr::Pointee<Metadata = ptr::DynMetadata<U>> + ?Sized + 'static,
>(
t: &T,
) -> Option<&U> {
// For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is
// only supported for sized types). The function therefore unconditionally
// returns `None` in that case.
let vtable: Option<ptr::DynMetadata<U>> =
const { TypeId::of::<T>().trait_info_of::<U>().as_ref().map(TraitImpl::get_vtable) };
match vtable {
Some(dyn_metadata) => {
let pointer = ptr::from_raw_parts(t, dyn_metadata);
let pointer = ptr::from_raw_parts(t as *const T as *const (), dyn_metadata);
// SAFETY: `t` is a reference to a type, so we know it is valid.
// `dyn_metadata` is a vtable for T, implementing the trait of `U`.
// `T` is sized here because `trait_info_of` only returns `Some` for sized types,
// so the thin data pointer fully describes the value.
Some(unsafe { &*pointer })
}
None => None,
Expand Down Expand Up @@ -1061,18 +1066,23 @@ pub const fn try_as_dyn<
#[must_use]
#[unstable(feature = "try_as_dyn", issue = "144361")]
pub const fn try_as_dyn_mut<
T: Any + 'static,
T: Any + ?Sized + 'static,
U: ptr::Pointee<Metadata = ptr::DynMetadata<U>> + ?Sized + 'static,
>(
t: &mut T,
) -> Option<&mut U> {
// For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is
// only supported for sized types). The function therefore unconditionally
// returns `None` in that case.
let vtable: Option<ptr::DynMetadata<U>> =
const { TypeId::of::<T>().trait_info_of::<U>().as_ref().map(TraitImpl::get_vtable) };
match vtable {
Some(dyn_metadata) => {
let pointer = ptr::from_raw_parts_mut(t, dyn_metadata);
let pointer = ptr::from_raw_parts_mut(t as *mut T as *mut (), dyn_metadata);
// SAFETY: `t` is a reference to a type, so we know it is valid.
// `dyn_metadata` is a vtable for T, implementing the trait of `U`.
// `T` is sized here because `trait_info_of` only returns `Some` for sized types,
// so the thin data pointer fully describes the value.
Some(unsafe { &mut *pointer })
}
None => None,
Expand Down
34 changes: 34 additions & 0 deletions tests/ui/any/try_as_dyn_unsized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//@ run-pass
#![feature(try_as_dyn)]

use std::fmt::Debug;

// Generic over `?Sized` T: relies on the relaxed bound on `try_as_dyn`.
fn try_debug<T: ?Sized + 'static>(t: &T) -> Option<String> {
std::any::try_as_dyn::<T, dyn Debug>(t).map(|d| format!("{d:?}"))
}

fn try_debug_mut<T: ?Sized + 'static>(t: &mut T) -> Option<String> {
std::any::try_as_dyn_mut::<T, dyn Debug>(t).map(|d| format!("{d:?}"))
}

fn main() {
// Sized case still works through a `?Sized` generic context.
let x: i32 = 7;
assert_eq!(try_debug(&x).as_deref(), Some("7"));

let mut y: i32 = 8;
assert_eq!(try_debug_mut(&mut y).as_deref(), Some("8"));

// Unsized `T` always returns `None`, even though `str: Debug` and
// `[T]: Debug` hold — vtable lookup for unsized impl types is not
// currently supported by `TypeId::trait_info_of`.
let s: &str = "hello";
assert!(try_debug::<str>(s).is_none());

let slice: &[i32] = &[1, 2, 3];
assert!(try_debug::<[i32]>(slice).is_none());

let dyn_any: &dyn std::any::Any = &0i32;
assert!(try_debug::<dyn std::any::Any>(dyn_any).is_none());
}
Loading