galemu 0.2.0

Provides a workaround for generic associated types (GAT) limited to lifetimes (GAL)
Documentation

Galemu   LatestVersion RustcVersion1.30+

galemu is a library to make it easy to work around not yet having generic associated types (GAT) wrt. lifetimes (GAL)


In current rust it can be tricky to abstract over some types due to not having generic associated types (GAT) in rust (yet). Often it is just necessary to have GAT wrt. liftimes and this crate provides some helpers to work around this.

Example Problem

For example you want to abstract over a (db) connection being able to open a transaction and that transaction being commitable. Once rust has GAT you would write following (stringly simplified, e.g. without returning results, additional methods etc.):

trait GenericConnection {
    type Transaction<'conn>: GenericTransaction;

    // the lifetime could have been omitted
    fn create_transaction<'s>(&'s self) -> Self::Transaction<'s>;
}

trait GenericTransaction {
    fn commit(self);
}

And then you could implement GenericTransaction for any Transaction<'a>.

But today you can only have following:

trait GenericConnection {
    type Transaction: GenericTransaction;

    // the lifetime could have been omitted
    fn create_transaction(&self) -> Self::Transaction;
}

trait GenericTransaction {
    fn commit(self);
}

But if you want to implement it for a Transaction<'conn> you can't as the 'conn needs to be bound to the lifetime of the create_transaction function.

Problem workaround

The idea of this crate is to lift the liftime into a know wraper type resulting in following setup:

use galemu::{Bound, BoundExt};

trait GenericConnection {
    type Transaction: GenericTransaction;

    // the lifetime could have been omitted
    fn create_transaction<'s>(&'s self) -> Bound<'s, Self::Transaction>;
}

trait GenericTransaction: for<'a> BoundExt {
    // on nightly use the "arbitrary self type" feature
    fn commit(me: Bound<'s, Self>);
}

And for implementing it you would use following:

use galemu::{create_gal_wrapper_type};

create_gal_wrapper_type!{
    /// Wraps `Transaction` erasing it's lifetime.
    ///
    /// This can only be used through a `Bound<'a, TransactionWrapper>` instance,
    /// as only then it is possible to access the wrapped type with the correct lifetime.
    struct TransactionWrapper(Transaction<'a>);
}

impl GenericConnection for Connection {
    type Transaction = TransactionWrapper;

    fn create_transaction(&mut self) -> Bound<Self::Transaction> {
        let trans = self.transaction();
        TransactionWrapper::new(trans)
    }
}

You can take a look at the module level documentation for a full example.