当我在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"));
}

...编译器返回没有意义的错误:

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绑定(bind)。那是因为这是最常见的情况-如果它不以这种方式运行,那么您将不得不在任何地方写自己的装订,这样会造成重复和嘈杂。

例如,您的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(动态大小类型)来调用它。

10-04 17:51