Skip to content

Commit 7acc268

Browse files
committed
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`. Tracking issue: #144361
1 parent a3e96d8 commit 7acc268

2 files changed

Lines changed: 48 additions & 4 deletions

File tree

library/core/src/any.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,18 +1007,23 @@ pub const fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
10071007
#[must_use]
10081008
#[unstable(feature = "try_as_dyn", issue = "144361")]
10091009
pub const fn try_as_dyn<
1010-
T: Any + 'static,
1010+
T: Any + ?Sized + 'static,
10111011
U: ptr::Pointee<Metadata = ptr::DynMetadata<U>> + ?Sized + 'static,
10121012
>(
10131013
t: &T,
10141014
) -> Option<&U> {
1015+
// For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is
1016+
// only supported for sized types). The function therefore unconditionally
1017+
// returns `None` in that case.
10151018
let vtable: Option<ptr::DynMetadata<U>> =
10161019
const { TypeId::of::<T>().trait_info_of::<U>().as_ref().map(TraitImpl::get_vtable) };
10171020
match vtable {
10181021
Some(dyn_metadata) => {
1019-
let pointer = ptr::from_raw_parts(t, dyn_metadata);
1022+
let pointer = ptr::from_raw_parts(t as *const T as *const (), dyn_metadata);
10201023
// SAFETY: `t` is a reference to a type, so we know it is valid.
10211024
// `dyn_metadata` is a vtable for T, implementing the trait of `U`.
1025+
// `T` is sized here because `trait_info_of` only returns `Some` for sized types,
1026+
// so the thin data pointer fully describes the value.
10221027
Some(unsafe { &*pointer })
10231028
}
10241029
None => None,
@@ -1061,18 +1066,23 @@ pub const fn try_as_dyn<
10611066
#[must_use]
10621067
#[unstable(feature = "try_as_dyn", issue = "144361")]
10631068
pub const fn try_as_dyn_mut<
1064-
T: Any + 'static,
1069+
T: Any + ?Sized + 'static,
10651070
U: ptr::Pointee<Metadata = ptr::DynMetadata<U>> + ?Sized + 'static,
10661071
>(
10671072
t: &mut T,
10681073
) -> Option<&mut U> {
1074+
// For unsized `T`, `trait_info_of` always returns `None` (vtable lookup is
1075+
// only supported for sized types). The function therefore unconditionally
1076+
// returns `None` in that case.
10691077
let vtable: Option<ptr::DynMetadata<U>> =
10701078
const { TypeId::of::<T>().trait_info_of::<U>().as_ref().map(TraitImpl::get_vtable) };
10711079
match vtable {
10721080
Some(dyn_metadata) => {
1073-
let pointer = ptr::from_raw_parts_mut(t, dyn_metadata);
1081+
let pointer = ptr::from_raw_parts_mut(t as *mut T as *mut (), dyn_metadata);
10741082
// SAFETY: `t` is a reference to a type, so we know it is valid.
10751083
// `dyn_metadata` is a vtable for T, implementing the trait of `U`.
1084+
// `T` is sized here because `trait_info_of` only returns `Some` for sized types,
1085+
// so the thin data pointer fully describes the value.
10761086
Some(unsafe { &mut *pointer })
10771087
}
10781088
None => None,

tests/ui/any/try_as_dyn_unsized.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//@ run-pass
2+
#![feature(try_as_dyn)]
3+
4+
use std::fmt::Debug;
5+
6+
// Generic over `?Sized` T: relies on the relaxed bound on `try_as_dyn`.
7+
fn try_debug<T: ?Sized + 'static>(t: &T) -> Option<String> {
8+
std::any::try_as_dyn::<T, dyn Debug>(t).map(|d| format!("{d:?}"))
9+
}
10+
11+
fn try_debug_mut<T: ?Sized + 'static>(t: &mut T) -> Option<String> {
12+
std::any::try_as_dyn_mut::<T, dyn Debug>(t).map(|d| format!("{d:?}"))
13+
}
14+
15+
fn main() {
16+
// Sized case still works through a `?Sized` generic context.
17+
let x: i32 = 7;
18+
assert_eq!(try_debug(&x).as_deref(), Some("7"));
19+
20+
let mut y: i32 = 8;
21+
assert_eq!(try_debug_mut(&mut y).as_deref(), Some("8"));
22+
23+
// Unsized `T` always returns `None`, even though `str: Debug` and
24+
// `[T]: Debug` hold — vtable lookup for unsized impl types is not
25+
// currently supported by `TypeId::trait_info_of`.
26+
let s: &str = "hello";
27+
assert!(try_debug::<str>(s).is_none());
28+
29+
let slice: &[i32] = &[1, 2, 3];
30+
assert!(try_debug::<[i32]>(slice).is_none());
31+
32+
let dyn_any: &dyn std::any::Any = &0i32;
33+
assert!(try_debug::<dyn std::any::Any>(dyn_any).is_none());
34+
}

0 commit comments

Comments
 (0)