Reputation: 79
I'm building a webapp in Rust and trying to implement basic Rails-style database migrations for managing my database. In my code, Migration
is a trait with up
and down
methods to apply and roll back the migration. Each individual database migration is a struct that implements the Migration trait. To keep track of database migrations in the correct order, I built a MigrationIndex
class.
struct MigrationIndex<T> {
migrations: Vec<Box<T>>
}
impl <T: Migration> MigrationIndex<T> {
// methods for managing the migrations...
}
impl <T: Migration> Default for MigrationIndex<T> {
pub fn default() -> MigrationIndex<T> {
MigrationIndex {
migrations: vec![]
}
}
}
So I go to use my class:
let migrations: MigrationIndex = Default::default();
But the compiler errors on this line with wrong number of type arguments: expected 1, found 0
. So I tried to add the missing trait parameter:
let migrations: MigrationIndex<Migration> = Default::default();
But on that line the compiler interprets Migration as a type, not a trait, and again fails to compile. At a guess I tried:
let migrations: MigrationIndex<T: Migration> = Default::default();
but that ends up being a syntax error. Now I'm stumped. If a type is parameterized by a trait, how do I specify that trait when I instantiate it?
Upvotes: 3
Views: 460
Reputation: 31273
When you use a generic, the generic argument needs to be of a single concrete type. This would cause all objects in the migrations Vec
to be of the same type. From your description it does not sound like that's what you want. You want a Vec
of different types that implement the same trait. This does not require generics:
#[derive(Default)]
struct MigrationIndex {
migrations: Vec<Box<Migration>>
}
impl MigrationIndex {
// methods for managing the migrations...
}
I also took the liberty of replacing your manual Default
impl with the equivalent automatically generated one through the derive
attribute.
In fact, in your previous implementation, the Box
was entirely unnecessary. If you have a concrete type, you can create a Vec
of elements of that type directly. Only when you want to put in different types implementing the same trait do you need the Box
, because these types might have different sizes.
Upvotes: 3
Reputation: 431819
When you specify a value for a generic type parameter, it has to be a concrete type, not a trait:
trait Migration {}
struct Foo;
impl Migration for Foo {}
fn main() {
let migrations: MigrationIndex<Foo> = Default::default();
}
Upvotes: 3