Faking inheritance - is it bad

#1

I’m playing with a wrapper around indexedb for wasm. In indexeddb, you are allowed to do different things on a database depending on what state it is in: you can only create object stores and indexes during an database upgrade.

I’m encoding this in the rust type system as follows:


#[repr(transparent)]
pub struct DbUpgrade {
    inner: web_sys::IdbDatabase,
}

impl DbUpgrade {
    // Stuff that can only be done during an upgrade
}

#[repr(transparent)]
pub struct Db {
    inner: web_sys::IdbDatabase,
}

impl Db {
    // Stuff that can be used at any time
}

// This is the tricky bit
impl Deref for DbUpgrade {
    type Target = Db;

    fn deref(&self) -> &Self::Target {
        unsafe { mem::transmute() }
    }
}

The transmute is valid, because both structs have identical internal layout.

Is the above correct? Is there a better way of achieving what I’m trying to achieve? I think web-sys uses the same approach.

0 Likes

#2

Why do you need Deref, first of all? If you just want to convert “upgraded” base into “ordinary”, when the upgrade ends, you should simply add this:

impl DbUpgrade {
    fn into_db(self) {
        Db { inner: self.inner }
    }
}

so that the “upgrading” base handler is consumed and ordinary one used. If you need to have all the methods on DbUpgrade and only subset of them on Db, I’d suggest another approach (just an idea, please don’t use it directly):

use std::marker::PhantomData;
pub struct Upgrading;
pub struct Db<T> {
    // inner: web_sys::IdbDatabase,
    inner: (),
    _phantom: PhantomData<T>,
}

impl<T> Db<T> {
    // Stuff that can be used at any time
    fn always(&self) {}
}

impl Db<Upgrading> {
    fn finish_upgrade(self) -> Db<()> { Db { inner: self.inner, _phantom: PhantomData } }
    // Stuff that can only be done during an upgrade
    fn on_upgrade(&self) {}
}

Playground with a small test

Or you always can do with trait bounds - just require that your DbUpgrade is a trait, which requires Db to be implemented, and implement them both on one struct and only Db on another.

0 Likes

#3

I like the idea of parameterizing by zero-sized types! I might try to implement this and see how it looks.

0 Likes