Abstract
This proposes allowing modules to be generic in order to reduce boilerplate:
mod self<Executor: Spawn>;
struct Foo {
executor: Executor
}
}
impl Foo {
// ...
}
would be equivalent to:
struct Foo<Executor: Spawn> {
executor: Executor
}
}
impl<Executor: Spawn> Foo<Executor> {
// ...
}
Motivation
There are cases when a crate author wants to support several strategies for the crate, but adding the appropriate generic that is repeated everywhere decreases readability. Examples may include:
- Geometry library wanting to abstract between
f32
andf64
- Async runtime - people usually want one per program
- Using
Rc
vsArc
for whole library. - Abstracting over string representation (
String
vs&str
vsCow<'a, str>
) - Combination of those above - a library may want to abstract over various orthogonal things!
There are currently two ways of doing it, each with its negatives:
- Generics over each type manually - lot of boilerplate, less readable
- Features - this is actually an anti-pattern as features must be additive, but people use is anyway
Proposal
Allow generics (with trait bounds) for the whole module, probably implemented as syntax sugar, inserting the generic into every struct, enum, type, trait and impl block. There are two ways of writing it:
-
mod self<T>;
- for file modules -
mod foo<T> { /*... */ }
- for submodules within a file
Using the modules:
-
use geometry::<f32>::Point;
- brings parametrized point in scope -
use awesome_http::<tokio::Executor>;
- bringsawesome_http
to scope. All accesses throughawesome_http
will have implicittokio::Executor
parameter. let user = my_lib::<String>::User::load("/var/lib/stuff/user")?;
Rules
- It's an error to import the module without the generic argument specified.
- It's an error to shadow type names.
- All module-level generic parameters can be thought of as being inserted at the beginning of generics of every item defined in the module
- Child modules inherit the generics via
super
Whaaat? This looks too strange, I don't like it!
Yes, I realize it looks strange and I don't know of any other language having this feature. Think of it as something similar to self
parameter. You may write dozen of functions, each having parameters name
, age
, address
, but what people usually do is to group them under a common struct that's used everywhere, where needed. In the generic world, the other extreme is avoiding implementing function parameters.