I'm having trouble understanding traits and object safety in Rust.
I have a StoreTrait
for storing some data and a Resource
struct that holds a reference to a StoreTrait
.
I want the Resource
to have a reference to a store
intance, because many of the methods of Resource
will use store
, and I don't want to explicitly pass store
to every method on Resource
.
I also need to have the logic reside in the trait, because I have various impls that will need to share it (an in-memory and an on-disk store). So moving it into the impl is not what I'd prefer.
In the Store
trait, I try passing &Self
to a function, but it fails because &Self
is not Sized
:
pub trait StoreTrait {
fn create_resource(&self) {
let agent = Resource::new(self);
}
}
struct Resource<'a> {
store: &'a dyn StoreTrait,
}
impl<'a> Resource<'a> {
pub fn new(store: &dyn StoreTrait) -> Resource {
Resource { store }
}
}
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/lib.rs:3:35
|
3 | let agent = Resource::new(self);
| ^^^^ doesn't have a size known at compile-time
|
= note: required for the cast to the object type `dyn StoreTrait`
help: consider further restricting `Self`
|
2 | fn create_resource(&self) where Self: Sized {
| ^^^^^^^^^^^^^^^^^
This is where this might become an XY problem
The compiler suggests using where Self: Sized
bounds in these methods.
However, this causes another problem later when calling save_resource()
from a Resource
, since that means I'm invoking a method on a trait object with a Sized
bound.
pub trait StoreTrait {
// So after adding the trait bounds here...
fn create_resource(&self)
where
Self: Sized,
{
let agent = Resource::new(self);
}
// And here (internal logic requires that)...
fn save_resource(&self, resource: Resource)
where
Self: Sized,
{
// This now requires `Self: Sized`, too!
self.create_resource()
}
}
pub struct Resource<'a> {
pub store: &'a dyn StoreTrait,
}
impl<'a> Resource<'a> {
pub fn new(store: &dyn StoreTrait) -> Resource {
Resource { store }
}
pub fn save(&self) {
self.store.save_resource(self)
}
}
error: the `save_resource` method cannot be invoked on a trait object
--> src/lib.rs:26:20
|
13 | Self: Sized;
| ----- this has a `Sized` requirement
...
26 | self.store.save_resource(self)
| ^^^^^^^^^^^^^
How do I circumvent setting the trait bound? Or how do I prevent calling a method on a trait object? Perhaps I'm doing something else that doesn't make a ton of sense?
Self: Sized
onsave_resource
? Why would you possibly want to prevent methods being called on trait objects, when you at the same time want to call a method on trait object (self.store
is a trait object, and you callsave_resource()
on it). – user4815162342Self: Sized
is required because it callsself.create_resource()
, which in turn requires sized. I'll update the question. The blanket implementation won't work, because various implementations of the Trait must be possible that have different logic. – joepioSelf: Sized
restriction) without getting to the bottom of it. – user4815162342