2

I want to write a basic abstraction of a database, using Rust traits.

pub trait Keyable {
    type Key;
    fn key(&self) -> &Self::Key;
}

and

// type alias for result - DbError are defined elsewhere
pub type Result<T> = Result<T, DbError>;

pub trait Database {
    type Item;

    fn get(&self, id: Keyable) -> Result<Self::Item>;    
    fn upsert(&self, item: Self::Item) -> Result<Keyable>;
}

I am struggling to express this: Database::Item must be at least Keyable.

There are two use cases:

  1. I can use some arbitrary type which knows how to return a key() in my Database::get(k: Keyable) function and yet return some Item (which is likely to have quite a bit more than just the key().
  2. In my fn Database::upsert() function, I want to be able to use the fact that the item is at least Keyable and therefore access the key() to lookup the database to make a decision if I simply insert or update.
  • 1
    [Something like this?](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=06e64add88155110ff7b1f9a0a39e790) – canton7 Mar 10 '22 at 17:10
  • 1
    Thank you. Exactly what I was looking for. I was missing the correct syntax to define bounds for associated types. – madison muir Mar 10 '22 at 21:21

1 Answers1

4

You can place a trait bound on an associated type:

pub trait Database {
    type Item: Keyable;

Then, you can use that trait's associated type(s) in function signatures:

    fn get(&self, id: &<Self::Item as Keyable>::Key) -> Result<Self::Item>;    
    fn upsert(&self, item: Self::Item) -> Result<<Self::Item as Keyable>::Key>;

(I also added an & because get() probably shouldn't be required to consume the key data.)

Putting that all together to a compilable example:

pub enum DbError { Something }
pub type Result<T> = std::result::Result<T, DbError>;

pub trait Keyable {
    type Key;
    fn key(&self) -> &Self::Key;
}

pub trait Database {
    type Item: Keyable;

    fn get(&self, id: &<Self::Item as Keyable>::Key) -> Result<Self::Item>;    
    fn upsert(&self, item: Self::Item) -> Result<<Self::Item as Keyable>::Key>;
}
Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
  • I was trying to add this type bound by doing ```type Item = Keyable``` for better portion of today. Obviously this did not work. And yes, get() should not consume the key, so the ref makes sense. Thank you. – madison muir Mar 10 '22 at 21:14