How do you create a Box when T is a trait-object?

后端 未结 3 1868
悲哀的现实
悲哀的现实 2021-01-14 05:42

I have the following code

extern crate rand;
use rand::Rng;

pub struct Randomizer {
    rand: Box,
}

impl Randomizer {
    fn new() -> Self {         


        
相关标签:
3条回答
  • 2021-01-14 06:21

    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, ...).

    0 讨论(0)
  • 2021-01-14 06:33

    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.

    0 讨论(0)
  • 2021-01-14 06:34

    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.

    0 讨论(0)
提交回复
热议问题