问号在类型参数中的含义是什么意思?

nal*_*ply 41 generics rust

我找到了以下定义std::borrow::BorrowMut:

pub trait BorrowMut<Borrowed>: Borrow<Borrowed>
where
    Borrowed: ?Sized,
{
    fn borrow_mut(&mut self) -> &mut Borrowed;
}
Run Code Online (Sandbox Code Playgroud)

Sized在这个类型参数bound(Borrowed: ?Sized)中,问号前面的问号是什么?

我咨询过:

但没有找到解释.请在答案中提供参考.


¹ 尤其见5.20性状
² 和部分6.1.9性状

She*_*ter 35

这意味着特征是可选的.当前语法是在DST语法RFC中引入的.

我所知道的唯一特征?Sized.

在这个具体的例子,我们可以实现BorrowMut未分级的类型,就像[T]-请注意,有没有&在这里!

一个内置实现利用了:

impl<T> BorrowMut<[T]> for Vec<T>
Run Code Online (Sandbox Code Playgroud)

正如Matthieu M.补充道:

这是一个扩大的范围 ; 一般而言,限制会产生更多的限制,但在Sized决定的情况下,除非另有说明,否则T将假定通用Sized.注意相反的方法是标记它?Sized("可能Sized").

  • 具体来说:这是*扩大界限*的情况; 通常边界强加*更多*约束但是在'Sized`的情况下,除非另有说明,否则通用的`T`将被假定为'Sized`并且注意它的方式是标记它? `(也许是'Sized`). (14认同)
  • @马修M。所以“!Sized”意味着严格不是“Sized”,而“T”意味着严格“Sized”,而“?Sized”意味着两者都是? (3认同)
  • @GuerlandoOCs:是的。 (3认同)

at5*_*321 18

这是基于示例的另一种解释,可能有助于理解这个概念,谢普马斯特和马蒂厄已经非常准确地解释了这个概念。

假设我们想要使用如下通用实现来创建一个特征:

pub trait MyTrait<T> {
    fn say_hi(&self) -> String;
}

impl<T> MyTrait<T> for &T {
    fn say_hi(&self) -> String {
        return "Hi!".to_string();
    }
}
Run Code Online (Sandbox Code Playgroud)

这将使我们能够调用say_hi()对各种类型的引用,如下所示:

let a = &74;  // a reference to a SIZED integer
println!("a: {}", a.say_hi());
let b = &[1, 2, 3];  // a reference to a SIZED array
println!("b: {}", b.say_hi());
Run Code Online (Sandbox Code Playgroud)

但是,如果我们声明一个这样的函数:

fn be_fancy(arr: &[u16]) {
    println!("arr: {}", arr.say_hi());
}
Run Code Online (Sandbox Code Playgroud)

,我们会得到一个编译器错误:

error[E0599]: the method `say_hi` exists for reference `&[u16]`, but its trait bounds were not satisfied
   |
   |     println!("arr: {}", arr.say_hi());
   |                             ^^^^^^ method cannot be called on `&[u16]` due to unsatisfied trait bounds
   |
note: the following trait bounds were not satisfied because of the requirements of the implementation of `MyTrait<_>` for `_`:
      `[u16]: Sized`
Run Code Online (Sandbox Code Playgroud)

可以看出,问题是我们的特征仅针对Sized类型的引用而实现。Sized是默认情况下“打开”的特殊标记特征。在大多数情况下这很方便,但有时我们可能想关闭该“限制”。?Sized基本上是说“类型可能会或可能不会调整大小”(这与“未调整大小”不同)。

我们的函数be_fancy需要引用一个未知(在编译时)大小的数组。要解决这个问题,我们可以简单地将T(相当于T: Sized)替换为T: ?Sized

pub trait MyTrait<T: ?Sized> {
    fn say_hi(&self) -> String;
}

impl<T: ?Sized> MyTrait<T> for &T {
    fn say_hi(&self) -> String {
        return "Hi yo!".to_string();
    }
}
Run Code Online (Sandbox Code Playgroud)