0
votes

Compiling the following code:

use std::rc::Rc;
use std::cell::RefCell;

pub enum StorageType {
    // ...
}

pub trait Storable {
    fn get_nblocks(&self) -> usize;
    fn get_storage(&self) -> Rc<RefCell<Storage>>;
}

pub trait Storage {
    fn store(&mut self, data: &Storable);
    fn get_type(&self) -> &StorageType;
}

pub struct DataVolume<T: Storage> {
    nblocks: usize,
    storage: Rc<RefCell<T>>,
}
impl<T: Storage> DataVolume<T> {
    pub fn new(nblocks: usize, storage: Rc<RefCell<T>>) -> DataVolume<T> {
        let this = DataVolume { nblocks: nblocks, storage: storage.clone() };
        storage.borrow_mut().store(&this);
        this
    }
}
impl<T: Storage> Storable for DataVolume<T> {
    fn get_nblocks(&self) -> usize {
        self.nblocks
    }
    fn get_storage(&self) -> Rc<RefCell<T>> {
        self.storage.clone()
    }
}

gives me:

src/store.rs:37:5: 39:6 error: method `get_storage` has an incompatible type
 for trait: expected trait store::Storage, found type parameter [E0053]
src/store.rs:37     fn get_storage(&self) -> Rc<RefCell<T>> {
src/store.rs:38         self.storage.clone()
src/store.rs:39     }
error: aborting due to previous error

I tried many things, and this is what I thought would finally be correct... Is my design of data structures itself wrong in the Rust world?

1
Well, keep in mind that in Rust, you must know the size of what you're returning during compile time. What exactly is size of <T:Storage> during compile time?Daniel Fath

1 Answers

2
votes

Your trait definition says:

fn get_storage(&self) -> Rc<RefCell<Storage>>

but your implementation is

fn get_storage(&self) -> Rc<RefCell<T>>

As the compiler is telling you - these are not compatible. The trait definition says that you will be returning an unsized type (which may or may not be possible, depending). The implementation says you are returning a specific type, but one that will be determined at compile time.

Let's pick the generic solution, as the compiler will monomorphize (generate specialized code for) each type you use. This can be faster but involve code bloat. Change your trait to:

pub trait Storable<T>
    where T: Storage
{
    fn get_nblocks(&self) -> usize;
    fn get_storage(&self) -> Rc<RefCell<T>>;
}

The whole program will then look like (playpen):

use std::rc::Rc;
use std::cell::RefCell;

pub enum StorageType {
    // ...
}

pub trait Storable<T>
    where T: Storage
{
    fn get_nblocks(&self) -> usize;
    fn get_storage(&self) -> Rc<RefCell<T>>;
}

pub trait Storage {
    fn store(&mut self, data: &Storable<Self>);
    fn get_type(&self) -> &StorageType;
}

pub struct DataVolume<T>
    where T: Storage
{
    nblocks: usize,
    storage: Rc<RefCell<T>>,
}

impl<T> DataVolume<T>
    where T: Storage
{
    pub fn new(nblocks: usize, storage: Rc<RefCell<T>>) -> DataVolume<T> {
        let this = DataVolume { nblocks: nblocks, storage: storage.clone() };
        storage.borrow_mut().store(&this);
        this
    }
}

impl<T> Storable<T> for DataVolume<T>
    where T: Storage
{
    fn get_nblocks(&self) -> usize {
        self.nblocks
    }
    fn get_storage(&self) -> Rc<RefCell<T>> {
        self.storage.clone()
    }
}

fn main() {}