Reputation: 4484
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?
edit: I ended up changing the arguments for the functions. Whenever I used &dyn StoreTrait
, I switched to &impl StoreTrait
. This means the functions with that signature are compiled for every implementation, which makes the binary a bit bigger, but it now works with the sized
requirement. yay!
Upvotes: 3
Views: 2770
Reputation: 577
Perhaps if you just move function from the trait to each implementation it will do what you want?
fn main() {}
pub trait StoreTrait {
fn create_resource(&self);
fn save_resource(&self, resource: &Resource);
}
struct Resource<'a> {
store: &'a dyn StoreTrait,
}
impl<'a> Resource<'a> {
pub fn new(store: &dyn StoreTrait) -> Resource {
Resource { store }
}
pub fn edit(&self) {
self.store.save_resource(self)
}
}
struct StoreMem {
resources: Vec<String>,
}
impl StoreTrait for StoreMem {
fn create_resource(&self) {
let agent = Resource::new(self);
}
fn save_resource(&self, resource: &Resource) {
//
}
}
struct StoreDisk {
resources: Vec<String>,
}
impl StoreTrait for StoreDisk {
fn create_resource(&self) {
let agent = Resource::new(self);
}
fn save_resource(&self, resource: &Resource) {
//
}
}
Upvotes: 0