Alessandro Tornesi
Alessandro Tornesi

Reputation: 93

How do I have a trait as a owned field of a struct?

I'm fairly new to Rust, I was tying to implement a struct that could have different structs that implemented a common trait as a field

In traditional programming languages with garbage collector I would have implemented it like this:


pub struct MemoryMapper {
    regions: Vec<Region>,
}

trait MemoryMappable: Sized {
    
}

pub struct Region {
    device: dyn MemoryMappable,
    start: u32,
    end: u32,
    remap: bool
}

But here I have the following compilation error:

the size for values of type `(dyn MemoryMappable + 'static)` cannot be known at compilation time
the trait `Sized` is not implemented for `(dyn MemoryMappable + 'static)`
only the last field of a struct may have a dynamically sized type
change the field's type to have a statically known size

I have tried making Sized a supertrait of MemoryMappable, but it still gives me issues with the following code:

pub fn map(&mut self, device: dyn MemoryMappable, start: u32, end: u32, remap: bool) -> &Region {
        self.regions.insert(0, Region{device, start, end, remap});
        return self.regions.get(0).unwrap();
}
the trait `MemoryMappable` cannot be made into an object
`MemoryMappable` cannot be made into an object

Because from Rust documentation about object safety

Sized must not be a supertrait. In other words, it must not require Self: Sized.

I have no idea of how to go about this at this point

Upvotes: 3

Views: 876

Answers (1)

cameron1024
cameron1024

Reputation: 10156

Fields of Sized structs can exist on the stack, and so must have a known size at compile time. This is what the Sized error is saying.

dyn Trait is "any type that implements Trait. So what is its size? Well, it depends on what the underlying type is.

Therefore, dyn Trait never implements Sized, even if Trait has Sized as a supertrait (all this does is guarantee that any type that implements Trait also implements Sized, but you still don't know which one it is).

In fact, making Sized a supertrait of Trait is how you opt-out of object safety, which makes it impossible to construct a dyn Trait.

Instead, the way you get around this is by using a pointer-like type:

  • Box<dyn Trait> creates an owned pointer
  • &dyn Trait creates a reference
  • Arc<dyn Trait> creates a reference counted pointer
  • etc.

The choice of pointer type depends on your use case, but in most scenarios, Box<dyn Trait> is fine, and is a good starting point.

Upvotes: 3

Related Questions