How to restrict generic implementation of a trait

2019-06-26 14:00发布

问题:

As an excuse to learn Rust, I'm working on code for genetic algorithms, and genetic programming later.

I declared a trait for mutation operations:

pub trait Mutator<IndvidualType> {
    fn mutate(&self, individual: &IndvidualType) -> IndvidualType;
}

It is easy to implement the trait for every single IndividualType, but I want something more general, a trait which is common for every list (vector) type genome, something like:

pub trait HasVectorGenome<IndividualType, BaseType>  {
    fn new_from_vec(genome: Vec<BaseType>) -> IndvidualType;
    fn get_vec(&self) -> Vec<BaseType>;
}

I want to have a generic mutator which is able to mutate every HasVectorGenome whose BaseType implements Rand (in order to be able to generate a new random value). Something like:

struct GeneralMutator;

impl<B, T> Mutator<T> for GeneralMutator
    where T: HasVectorGenome<T, B>,
          B: Rand
{
    fn mutate(&self, individual: &T) -> T {
        let genome: Vec<B> = individual.get_vec();
        genome[0] = rand::random::<B>();
        T::new_from_vec(genome)
    }
}

I've got the error the type parameter `B` is not constrained by the impl trait, self type, or predicates, and I can't compile. I do not know how to express this correctly.

回答1:

I've put a complete working version of this code on the playground (except that I stubbed out the random parts).

First, I removed the IndividualType parameter from HasVectorGenome. This is simply the type for which the trait is implemented, and your definition of the trait is inconsistent about this (new_from_vec returns IndividualType but get_vec consumes Self).

Second, I made BaseType an associated type, meaning that there is a single unique base type for any individual type. This is technically a restriction, but in most circumstances you don't need the flexibility and it makes the types simpler (and is in fact the primary change needed to get rid of the error you're seeing). So the trait is now:

pub trait HasVectorGenome  {
    type BaseType;
    fn new_from_vec(genome: Vec<Self::BaseType>) -> Self;
    fn get_vec(&self) -> Vec<Self::BaseType>;
}

Then, I adjusted the where clause of the GeneralMutator implementation:

impl<T> Mutator<T> for GeneralMutator
  where T: HasVectorGenome,
        T::BaseType : Rand


标签: generics rust