Confused about the Sized trait

The Rust Book gives an example of the Sized trait:

fn generic<T: ?Sized>(t: &T) {}

But what's the point of this? You already have to put DSTs behind references by default, so why add this extra syntax? Something like

fn generic(t: T) {}

is already callable with &str, &[T], etc.

Taking a sized T by value is not the same as taking a reference to a potentially unsized T. Note that references are their own distinct concrete types, and not just a passing convention or whatnot. Moreover, other types support unsized generics, like Box<_> for instance.

Here's a quick demonstration.

trait Trait {}

impl Trait for str {}
impl Trait for () {}

// This works
fn example<T: ?Sized + Trait>(_shared_ref: &T) {}

// These don't
// fn example<T: Trait>(_owned_t: T) {}
// fn example<T: Trait>(_shared_ref_to_sized_t: &T) {}

fn main() {
    example("");
    example(&() as &dyn Trait);
}
4 Likes
fn generic<T: ?Sized>(t: &T) {}

This code can only accept references. For example, you can't make it consume a Vec<i32>. Compared to the code that just accepts a t: T, the above code is more restrictive in what it can accept. This can be done if the function body can only handle references and nothing else.

2 Likes