Module wide type-parameters, thoughts

I support this.

I'm working on an interpreter. Programs read streams of bytes and write streams of bytes, but rather than forcing STDIN and STDOUT for IO, I'd like it to be parameterized over two types R: std::io::Read and W: std::io::Write, as this makes things such as testing easier. This means that almost every struct I'm defining in the program includes an <R: Read, W: Write>, which is quite noisy. I'd prefer if I could simply make the module parameterized over R and W.

I'd have two syntaxes for declaring parameters:

  • You can declare parameters from anywhere inside a module by writing

    mod<R: Read, W: Write>;
    
  • A module defined with the mod foo { } syntax can be given parameters as

    mod foo<R, W> { }
    

Say we have a parameterized module foo<R: Read, W: Write> defined in some other file. It would be imported just like any other module:

mod foo;

Any attempt to actually access items in this module, however, would require that it is somehow disambiguated:

    foo::<i32>::bar();
    use foo::<Option<Vec<usize>>>::Baz;

In the example for the interpreter, main.rs would look like:

use std::io::*;

mod interpreter;

fn main() {
    let src: String = /* read a file */;
    interpreter::<Stdin, Stdout>::interpret(&src, stdin(), stdout());
}

#[cfg(test)]
mod tests {
    fn run_and_collect_output(src: &str,
                              input: &str) -> Option<String> {
        let output: Vec<u8> = Vec::new();
        interpreter::<&[u8], Vec<u8>>
                   ::interpret(src, input.as_bytes(), output);
        output
    }
    #[test]
    fn test() { ... }
}

Perhaps there could be some inference for this, the way there is for type parameters in functions. That way perhaps ::<Stdin, Stdout> and ::<&[u8], Vec<u8>> could be omitted.

In addition (or alternatively), perhaps there should be some way to "distribute" the type parameters to a module's constituent items. For example, say a math3d module was parameterized over F: Float and exposed

pub struct Point(F, F, F);
pub fn distance(p1: Point, p2: Point) { ... }

It should be possible to turn math3d's type parameter into a type parameter of distance so that you could import it generically.

use<F> math3d::<F>::distance;
/* `distance` will now behave as if it had a type parameter F */

To import math3d in such a way that all of its items had its type parameters distributed to them, you could write:

use<F> math3d::<F>;

This way, math3d would become unparameterized and all of its items would take the parameter instead (the way the module would be written today).

Syntax is up for bikeshedding, but that's the general idea.

1 Like