简体   繁体   中英

Rust unable to infer return type for generic trait impl

I am once again having fun time trying to satisfy the type checker in Rust. Here is vaguely what I have with code examples coming up. I have a trait, whose methods take some number of arguments, and in order to have flexibility I wanted to be able for parameters to be of specific types, in any possible combination, as long as associated type of passed in type implements a given trait.

Note the type parameters A and B of trait represent inputs to functions. Now I had a version where these parameters were generic parameters of functions, but that does not work, as other implementations of this trait rely on knowing which inputs it takes, so they have to be part of trait generic, and they can not be moved down into generic functions.

Here is what the trait looks like:

pub trait Filter<T: Scalar + Lapack, A: Data<Elem=T>, B: Data<Elem=T>> {
    type Prediction;
    type Update;
    fn predict(&self, states: &ArrayBase<A, Ix2>, covariances: &ArrayBase<B, Ix3>) -> Self::Prediction;
    fn update(&self, states: &ArrayBase<A, Ix2>, covariances: &ArrayBase<A, Ix3>, measurements: &ArrayBase<A, Ix2>) -> Self::Update;
}

And then I have a struct, which implements this trait and also does not care which inputs are given to the trait methods as long as their associated type implement the given trait, so here is what the struct and impl look like:

pub struct KalmanFilter<T: Scalar + Lapack> {
    transition_matrix: Array2<T>,
    observation_matrix: Array2<T>,
    transition_covariance: Array2<T>,
    observation_covariance: Array2<T>,
}

impl<T: Scalar + Lapack, A: Data<Elem=T>, B: Data<Elem=T>> Filter<T, A, B> for KalmanFilter<T> {

    type Prediction = (ArrayBase<OwnedRepr<T>, Ix2>, ArrayBase<OwnedRepr<T>, Ix3>);
    type Update = (ArrayBase<OwnedRepr<T>, Ix3>, ArrayBase<OwnedRepr<T>, Ix3>);

    fn predict(&self, states: &ArrayBase<A, Ix2>, covariances: &ArrayBase<B, Ix3>) -> Self::Prediction {
        ......... code ..... code
    }

    fn update(
        &self,
        states: &ArrayBase<A, Ix2>,
        covariances: &ArrayBase<A, Ix3>,
        measurements: &ArrayBase<A, Ix2>,
    ) -> Self::Update {
        ......... code ..... code
}

And now we come to the error message. Namely when I try to call the update method from struct above I get:

error[E0284]: type annotations needed: cannot satisfy `<_ as RawData>::Elem == f64`
   --> src/filter/kalman.rs:320:20
    |
320 |         let a = kf.update(&states, &covariances, &measurements);
    |                    ^^^^^^ cannot satisfy `<_ as RawData>::Elem == f64`
    |
    = note: required because of the requirements on the impl of `filter_traits::Filter<f64, OwnedRepr<f64>, _>` for `kalman::KalmanFilter<f64>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0284`.
error: could not compile `rusty_rudolf`

Now this is extra interessting, since if I do monomophization by hand, and basically in impl block I replace the type parameter T with f64 it just works. Why does this not work, and why does it work with manual monomorphization?

Edit: One more thing, if I give up the ability to have combination of types as inputs, and if I delete type parameter B in the trait above, it then works. Somehow the introduction of the second type parameter, leads to issues.

The problem is probably for the second function, update() , it couldn't infer the type of B , since it does not refer to the type B in the function signature. Looking at the signature for predict , you probably want to use the type B for covariances in the update function.

fn update(
        &self,
        states: &ArrayBase<A, Ix2>,
        covariances: &ArrayBase<B, Ix3>, // use B instead of A
        measurements: &ArrayBase<A, Ix2>,
    ) -> Self::Update {
...    
}

Edit: One more thing, if I give up the ability to have combination of types as inputs, and if I delete type parameter B in the trait above, it then works. Somehow the introduction of the second type parameter, leads to issues.

Imagine you have two implementations of the trait with the same T and A and different B . How would the compiler know which one to use?

And lastly how would one go about solving this? Is there any way to say that type parameters are completely independent, or at least to say that associated type is only dependent on first type parameter?

It seems reasonable to split your trait into 2:

pub trait FilterUpdate<T: Scalar + Lapack, A: Data<Elem=T>> {
    type Update;
    fn update(&self, states: &ArrayBase<A, Ix2>, covariances: &ArrayBase<A, Ix3>, measurements: &ArrayBase<A, Ix2>) -> Self::Update;
}

pub trait Filter<T: Scalar + Lapack, A: Data<Elem=T>, B: Data<Elem=T>> : FilterUpdate<T, A> {
    type Prediction;
    fn predict(&self, states: &ArrayBase<A, Ix2>, covariances: &ArrayBase<B, Ix3>) -> Self::Prediction;
}

Then you can have only one implementation of update for a specific A . Or maybe make B an associated type in the first place and not a type parameter of the trait.

Or you could keep the current API and specify the trait type parameters explicitly. Based on the error message I guess it would look like this:

let a = <KalmanFilter<f64> as Filter<f64, OwnedRepr<f64>, ???>>::update(&kf, &states, &covariances, &measurements)

where you need to decide what ??? is.

  1. I guess you'd end up needing to do that for most update calls.

  2. A helper function could simplify the calls, especially if I understand correctly that it generally doesn't matter what B is and you can take it to equal A :

     fn update<T: Scalar + Lapack, A: Data<Elem=T>, F: Filter<T, A, A>>(filter: &F, states: &ArrayBase<A, Ix2>, covariances: &ArrayBase<A, Ix3>, measurements: &ArrayBase<A, Ix2>) -> F::Update { filter.update(states, covariances, measurements) }

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