When I run the following code on Rust playground...
fn take_part_1<'a>(s: &'a str) -> &'a str {
s.split(':').next().unwrap()
}
fn take_part_2<'a, T: 'a + AsRef<str>>(s: &'a T) -> &'a str {
let b = s.as_ref().split(':').next().unwrap();
b
}
fn main() {
println!("{}", take_part_1("a:b"));
println!("{}", take_part_2("a:b"));
}
... the compiler returns an error that doesn't make sense:
12 | println!("{}", take_part_2("a:b"));
| ^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
I can fix it by adding ?Sized like so:
fn take_part_2<'a, T: 'a + AsRef<str> + ?Sized>(s: &'a T) -> &'a str {
Why is this ?Sized required (and why does the compiler point the error at the function call)? What does it do? Shouldn't I be able to pass the reference to an unsized object and have it just work?
What confuses me is that the non-generic implementation works as you'd expect, with no requirements for ?Sized (even though the compiler has pointed out that str isn't Sized!)
Most places where you use a type parameter, Rust will implicitly insert an additional
Sizedbound. That's because it is the most common case - if it didn't behave that way then you'd have to write the bound yourself everywhere and it would get repetitive and noisy.For example, your
take_partfunction is exactly equivalent to this:However, your function's implementation does not require
Tto beSizedbecause it only ever uses it by reference. Adding: ?Sizedeffectively removes that implicit bound, communicating to the type checker that the size ofTdoesn't need to be known, so your function is as general as possible. In this case, making it more general allows it to be called withTasstr, a dynamically sized type.