6

I have the following code

extern crate rand;
use rand::Rng;

pub struct Randomizer {
    rand: Box<Rng>,
}

impl Randomizer {
    fn new() -> Self {
        let mut r = Box::new(rand::thread_rng()); // works
        let mut cr = Randomizer { rand: r };
        cr
    }

    fn with_rng(rng: &Rng) -> Self {
        let mut r = Box::new(*rng); // doesn't work
        let mut cr = Randomizer { rand: r };
        cr
    }
}

fn main() {}

It complains that

error[E0277]: the trait bound `rand::Rng: std::marker::Sized` is not satisfied
  --> src/main.rs:16:21
   |
16 |         let mut r = Box::new(*rng);
   |                     ^^^^^^^^ `rand::Rng` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `rand::Rng`
   = note: required by `<std::boxed::Box<T>>::new`

I don't understand why it requires Sized on Rng when Box<T> doesn't impose this on T.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
Andreas
  • 6,447
  • 2
  • 34
  • 46

3 Answers3

7

More about the Sized trait and bound - it's a rather special trait, which is implicitly added to every function, which is why you don't see it listed in the prototype for Box::new:

fn new(x: T) -> Box<T>

Notice that it takes x by value (or move), so you need to know how big it is to even call the function.

In contrast, the Box type itself does not require Sized; it uses the (again special) trait bound ?Sized, which means "opt out of the default Sized bound":

pub struct Box<T> where T: ?Sized(_);

If you look through, there is one way to create a Box with an unsized type:

impl<T> Box<T> where T: ?Sized
....
    unsafe fn from_raw(raw: *mut T) -> Box<T>

so from unsafe code, you can create one from a raw pointer. From then on, all the normal things work.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Chris Emerson
  • 13,041
  • 3
  • 44
  • 66
3

The problem is actually quite simple: you have a trait object, and the only two things you know about this trait object are:

  • its list of available methods
  • the pointer to its data

When you request to move this object to a different memory location (here on the heap), you are missing one crucial piece of information: its size.

How are you going to know how much memory should be reserved? How many bits to move?

When an object is Sized, this information is known at compile-time, so the compiler "injects" it for you. In the case of a trait-object, however, this information is unknown (unfortunately), and therefore this is not possible.

It would be quite useful to make this information available and to have a polymorphic move/clone available, but this does not exist yet and I do not remember any proposal for it so far and I have no idea what the cost would be (in terms of maintenance, runtime penalty, ...).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
1

I also want to post the answer, that one way to deal with this situation is

fn with_rng<TRand: Rng>(rng: &TRand) -> Self {
    let r = Box::new(*rng);
    Randomizer { rand: r }
}

Rust's monomorphism will create the necessary implementation of with_rng replacing TRand by a concrete sized type. In addition, you may add a trait bound requiring TRand to be Sized.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Andreas
  • 6,447
  • 2
  • 34
  • 46