Should downcast_ref and downcast_mut not require Sized?

The Any trait has two methods to take immutable and mutable references of downcast_ref and downcast_mut. These functions use the downcast_ref_uncheckedfunction under the hood and its implementation seems to be pure computation of pointers. I feel like we can remove theSized` trait bound from these functions. What do you think?

How do you propose the fat pointer be constructed?

1 Like

The cast *const dyn Any to *const T is not valid if T is not Sized.

1 Like

in addition to what's already said, you cannot coerce unsized types such as str into dyn Any in the first place anyway, it makes no sense for downcast_ref() to support unsized types.

here's a paragraph from the documentation of Unsize (with added emphasis):

A type implements Unsize<dyn Trait + 'a> if all of these conditions are met:

  • The type implements Trait.
  • Trait is dyn-compatible
  • The type is sized.
  • The type outlives 'a
2 Likes

Expanding a bit, this is a consequence of how Rust's unsized types are implemented: Their size information is stored as an extra field of the pointer itself, making it a pointer-length pair (for slices) or a pointer-vtable pair (for trait objects). To convert a str or other slice into a trait object, you would need to store the length somewhere in order to prevent out-of-bounds accesses but there's no place to do that:

  • It can't go beside the slice data, because it might be a subslice of something else
  • It can't go in the vtable, because that's statically allocated and shared across multiple instances of the same type
  • It can't be an additional extra field on the pointer, because then the pointer itself becomes unsized (because you can potentially continue this recursively as many times as you want)
3 Likes

Thank you for the very quick responses, everyone! I totally forgot and never thought about fat pointers with vtables. Thank you also for the detailed descriptions.

you cannot coerce unsized types such as str into dyn Any in the first place anyway

To convert a str or other slice into a trait object, you would need to store the length somewhere in order to prevent out-of-bounds accesses but there's no place to do that:

I didn't know this at all. I thought that the upcast was at least possible.

I'm gonna just use some owned representation of data for these types not sized in my project then.

dyn Traits themselves are actually Unsize<dyn Trait> (for any upcastable target), yeah.

But the way that Any works is that if I type erase coerce (), these all call ()'s type_id method...

  • dyn Any + Send + Sync
  • dyn Any + Send
  • dyn Any + Sync
  • dyn Any

...and the same is true of subtraits of Any too. And upcasting doesn't change this.

1 Like