为什么需要 ?Sized 来传递对泛型类型参数的引用?
Why is ?Sized required to pass a reference to a generic type parameter?
当我在 Rust 操场上 运行 以下代码时...
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"));
}
...编译器returns 一个没有意义的错误:
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`
我可以像这样添加 ?Sized
来修复它:
fn take_part_2<'a, T: 'a + AsRef<str> + ?Sized>(s: &'a T) -> &'a str {
为什么需要这个 ?Sized
(为什么编译器将错误指向函数调用)?它有什么作用?难道我不能将引用传递给未确定大小的对象并让它正常工作吗?
让我感到困惑的是,非泛型实现如您所期望的那样工作,没有 ?Sized
的要求(即使编译器指出 str 不是 Sized
! )
大多数使用类型参数的地方,Rust 会隐式插入一个额外的 Sized
绑定。那是因为它是最常见的情况——如果它没有那样做,那么你必须自己写边界到处,它会变得重复和嘈杂。
例如,您的 take_part
函数完全等同于:
fn take_part_2<'a, T: 'a + AsRef<str> + Sized>(s: &'a T) -> &'a str {
let b = s.as_ref().split(':').next().unwrap();
b
}
但是,您函数的 实现 并不要求 T
为 Sized
,因为它仅通过引用使用它。添加 : ?Sized
有效地移除了隐式绑定,向类型检查器传达不需要知道 T
的大小,因此您的函数尽可能通用。在这种情况下,使其更通用,可以使用 T
作为动态大小的类型 str
调用它。
当我在 Rust 操场上 运行 以下代码时...
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"));
}
...编译器returns 一个没有意义的错误:
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`
我可以像这样添加 ?Sized
来修复它:
fn take_part_2<'a, T: 'a + AsRef<str> + ?Sized>(s: &'a T) -> &'a str {
为什么需要这个 ?Sized
(为什么编译器将错误指向函数调用)?它有什么作用?难道我不能将引用传递给未确定大小的对象并让它正常工作吗?
让我感到困惑的是,非泛型实现如您所期望的那样工作,没有 ?Sized
的要求(即使编译器指出 str 不是 Sized
! )
大多数使用类型参数的地方,Rust 会隐式插入一个额外的 Sized
绑定。那是因为它是最常见的情况——如果它没有那样做,那么你必须自己写边界到处,它会变得重复和嘈杂。
例如,您的 take_part
函数完全等同于:
fn take_part_2<'a, T: 'a + AsRef<str> + Sized>(s: &'a T) -> &'a str {
let b = s.as_ref().split(':').next().unwrap();
b
}
但是,您函数的 实现 并不要求 T
为 Sized
,因为它仅通过引用使用它。添加 : ?Sized
有效地移除了隐式绑定,向类型检查器传达不需要知道 T
的大小,因此您的函数尽可能通用。在这种情况下,使其更通用,可以使用 T
作为动态大小的类型 str
调用它。