简体   繁体   中英

Instantiating a struct parameterized by a trait

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?

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();
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM