82

I found the definition for std::borrow::BorrowMut:

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

What does the question mark in front of Sized mean in this type parameter bound (Borrowed: ?Sized)?

I consulted:

but didn't find an explanation. Please give a reference in your answer.


¹ especially see section 5.20 Traits
² and section 6.1.9 Traits
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
nalply
  • 26,770
  • 15
  • 78
  • 101

2 Answers2

64

It means that the trait is optional. The current syntax was introduced in the DST syntax RFC.

The only trait I am aware of that works for ? is Sized.

In this specific example, we can implement BorrowMut for unsized types, like [T] — note that there's no & here!

One built-in implementation makes use of that:

impl<T> BorrowMut<[T]> for Vec<T>

As Matthieu M. adds:

This is a case of a widening bound; in general bounds impose more constraints, but in the case of Sized it was decided that unless otherwise noted a generic T would be assumed to be Sized. The way to note the opposite would be to mark it ?Sized ("maybe Sized").

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 23
    Specifically: this is a case of *widening bound*; in general bounds impose *more* constraints however in the case of `Sized` it was decided that unless otherwise noted a generic `T` would be assumed to be `Sized` and the way to note it would be to mark it `?Sized` (maybe `Sized`). – Matthieu M. May 20 '15 at 08:19
  • 2
    @MatthieuM. so `!Sized` would mean strictly not `Sized`, while `T` means strictly `Sized`, and `?Sized` means both? – Guerlando OCs Jun 26 '21 at 04:42
  • 3
    @GuerlandoOCs: Yes. – Matthieu M. Jun 26 '21 at 09:34
12

Here is an alternative explanation, based on an example, which might be helpful in understanding the concept, which was already explained very accurately by Shepmaster and Matthieu.

Let's say we want to make a trait with a generic implementation like this:

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

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

That will allow us to call say_hi() on references to various types like this:

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());

However, if we declare a function like this:

fn be_fancy(arr: &[u16]) {
    println!("arr: {}", arr.say_hi());
}

, we will get a compiler error:

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`

As can be seen, the problem is our trait is only implemented for references to Sized types. Sized is a special marker trait that is "on" by default. In most cases that's convenient, but sometimes we might want to turn that "restriction" off. ?Sized basically says "the type may or may not be Sized" (which is not the same as "unsized").

Our function be_fancy expects a reference to an array of unknown (at compile time) size. To fix the problem, we can simply replace T (which is equivalent to T: Sized) with T: ?Sized like this:

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();
    }
}
at54321
  • 8,726
  • 26
  • 46
  • 1
    > Sized is a special marker trait that is "on" by default. In most cases that's convenient Is there an easy to understand example where `Sized` being "on" by default has a convenience benefit? (trying to understand) – Venryx Dec 19 '22 at 19:22
  • 1
    @Venryx Writing `fn generic(t: T)` is the same as `fn generic(t: T)`. Were it the same as `fn generic(t: T)`, then it wouldn't compile. Why? Well, take the return value, for example. Rust needs to _know_ its size at compile time. If you really needed to return a type of unknown size, you'd generally use some kind of a pointer (a pointer's size on the **stack** is known at compile time). – at54321 Dec 19 '22 at 19:56