Rust 无法推断通用特征 impl 的返回类型

Posted

技术标签:

【中文标题】Rust 无法推断通用特征 impl 的返回类型【英文标题】:Rust unable to infer return type for generic trait impl 【发布时间】:2021-04-15 07:36:26 【问题描述】:

我又一次尝试满足 Rust 中的类型检查器的乐趣。这是我对即将出现的代码示例的模糊看法。我有一个特征,它的方法需要一些参数,并且为了具有灵活性,我希望能够以任何可能的组合将参数设为特定类型,只要传入类型的关联类型实现给定的特征.

注意 trait 的类型参数 AB 表示函数的输入。现在我有一个版本,其中这些参数是函数的通用参数,但这不起作用,因为此 trait 的其他实现依赖于知道它需要哪些输入,因此它们必须是 trait generic 的一部分,并且它们不能移动下到泛型函数。

这是特征的样子:

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;

然后我有一个结构体,它实现了这个特征并且也不关心哪些输入被赋予特征方法,只要它们的关联类型实现给定的特征,所以这是结构和实现的样子:

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

现在我们来看错误消息。也就是说,当我尝试从上面的结构中调用更新方法时,我得到:

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`

现在这很有趣,因为如果我手动进行单模化,并且基本上在 impl 块中,我将类型参数 T 替换为 f64 它就可以工作。为什么这不起作用,为什么它对手动单态化起作用?

编辑:还有一件事,如果我放弃将类型组合作为输入的能力,并且如果我在上面的特征中删除类型参数B,它就会起作用。不知何故,第二个类型参数的引入会导致问题。

【问题讨论】:

【参考方案1】:

问题可能出在第二个函数update() 上,它无法推断出B 的类型,因为它在函数签名中没有引用B 的类型。查看predict 的签名,您可能希望在update 函数中为covariances 使用B 类型。

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

【讨论】:

但是为什么会这样呢?第二个函数根本不使用B 类型,而且AB 类型根本不会影响函数的输出,因为返回类型不依赖于这些类型参数。请注意,如果我添加更多类型参数,情况会变得更糟。我希望每个方法的每个参数都有一个类型参数,只是为了让我可以拥有响应所有可能的输入组合的函数。 哦,我想我明白了。是不是像下面这样:因为方法update没有使用B类型,那么调用kf.update不知道使用哪个实现,因为它可能使用任何实现,因此它根本拒绝使用任何实现? 最后,如何解决这个问题?有没有办法说类型参数是完全独立的,或者至少说关联类型只依赖于第一个类型参数? 老兄,我是单身人士的一个懒惰初始化者。我在看你生锈的答案,一个真诚的问题:值得学习吗?您真诚的意见。【参考方案2】:

编辑:还有一件事,如果我放弃将类型组合作为输入的能力,并且如果我在上面的特征中删除类型参数 B,它就会起作用。不知何故,第二个类型参数的引入会导致问题。

假设您有两个具有相同TA 和不同B 的特征实现。编译器如何知道使用哪一个?

最后,如何解决这个问题?有没有办法说类型参数是完全独立的,或者至少说关联类型只依赖于第一个类型参数?

把你的特质分成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;

那么对于特定的A,您只能有一个update 的实现。或者可能首先将 B 设为关联类型,而不是 trait 的类型参数。

或者您可以保留当前的 ​​API 并明确指定特征类型参数。根据错误消息,我猜它看起来像这样:

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

您需要在哪里确定??? 是什么。

    我猜你最终需要为大多数 update 调用这样做。

    辅助函数可以简化调用,特别是如果我正确理解B 是什么通常并不重要,您可以将其等同于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)
    
    

【讨论】:

这也是一个很好的解决方案,我也想过,但是开销和类型的数量实在是太荒谬了。无论如何,我不希望调用者将它与一些奇怪的类型组合一起使用。谢谢。 我还添加了有关如何显式指定类型参数的信息。

以上是关于Rust 无法推断通用特征 impl 的返回类型的主要内容,如果未能解决你的问题,请参考以下文章

在 Rust 中的 iter 上使用 map 时,“无法推断 `_` 的类型”

Rust impl trait

TypeScript——类型检查机制

是否可以在特征定义中使用“impl Trait”作为函数的返回类型?

推断函子返回类型的通用方法?

通用扩展方法:无法从用法推断类型参数