Is there any way to achieve module level opaque types?

I was trying to write a implementation agnostic database wrapper.
But different databases uses different types for primary keys. ie, SQL usually uses some kind of integers, but MongoDB uses ObjectId.
So, is there anyway to define the Id field as an opaque type?

For example:

mod interface {

    type Id;

    struct User {
        id: Id,
        name: String

    trait UserRepo {
        create(&self, name: String) -> Id;
        delete(&self, id: Id);

mod impl {
    type Id = i64;

    // .. implementation of UserRepo ..

Sounds like the perfect task for parametric polymorphism. I usually like to define my database keys as Ord + Hash + Clone:

  • Ord for DBs that need ordering of keys (mostly used for indexing);
  • Hash for other DBs that need hashing (again, for indexing or random access for query lookup),
  • Clone for a dash of interoperability, and because most key types are conceptually atomic anyway (e.g. strings, integers, UUIDs, etc.)
1 Like

How to implement this?

Use trait

trait Database {
    type Id: Clone + Ord + Hash;
    // other functionality

struct User<D: Database> {
    id: D::Id,
    name: String

Then have different database backends implement Database