Module Traits


#1

Recently, I’ve been having to write a lot of contract-driven code. I’ve been needing to use conditional compilation upon modules, ensuring that the modules provide the same external interface. Unfortunately, modules do not have traits, so I’ve had to reinterpret ‘modules’ as ‘static struct types’. For example:

trait MyTrait {
    fn foo();
}

struct A {}
struct B {}

impl MyTrait for A {
    fn foo() {
        // Do something
    }
}

impl MyTrait for B {
    fn foo() {
        // Do something else
    }
}

#[cfg(config_variable = "A")] use A as MyFakeModule;
#[cfg(config_variable = "B")] use B as MyFakeModule;

fn do_something() {
    MyFakeModule::foo();
}

Here, A and B are contractually obliged to implement the same ‘MyTrait’ interface. There are a few problems with this approach:

  1. There’s a useless empty struct declaration
  2. The syntax use MyFakeModule::x::y::z; doesn’t work, with complaints that MyFakeModule is not a module.
  3. I run into this problem: https://github.com/rust-lang/rust/issues/38078

This would be 10 times easier to do if modules could implement traits, or if there way such a thing as ‘module traits’. Alternatively, is there another way to improve this?


#2

The standard way of doing this seems to be to have two modules that are #[cfg]d to only load one or the other and named the same. Unfortunately this doesn’t/cannot provide a language guarantee that they provide the same interface, so you have to rely on testing over any combinations of config that matter.


#3

It’s not perfect but how about a macro?

One advantage is you get to write doc comments once instead of for each different implementation of the “module trait.”

macro_rules! my_module {
    {
        pub fn foo() {
            $($foo:tt)*
        }

        // private helpers or whatever at the end
        $($rest:item)*
    } => {
        pub mod my_module {
            /// Documentation comment for foo.
            pub fn foo() {
                $($foo)*
            }

            $($rest)*
        }
    };
}

#[cfg(config_variable = "A")]
my_module! {
    pub fn foo() {
        // Do something
    }
}

#[cfg(config_variable = "B")]
my_module! {
    pub fn foo() {
        // Do something else
    }
}