Relax T: Sized bound on try_as_dyn / try_as_dyn_mut

`trait_info_of` already returns `None` for unsized types, so allowing
`T: ?Sized` is sound and lets callers in generic contexts use these
functions without a separate `Sized` bound. For unsized `T`, the
function always returns `None`.
This commit is contained in:
aobatact
2026-05-03 16:08:11 +09:00
parent a3e96d8cb8
commit ecf527faaf
2 changed files with 48 additions and 4 deletions
+14 -4
View File
@@ -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,
@@ -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,
+34
View File
@@ -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());
}