Rust 无法推断通用特征 impl 的返回类型
Posted
技术标签:
【中文标题】Rust 无法推断通用特征 impl 的返回类型【英文标题】:Rust unable to infer return type for generic trait impl 【发布时间】:2021-04-15 07:36:26 【问题描述】:我又一次尝试满足 Rust 中的类型检查器的乐趣。这是我对即将出现的代码示例的模糊看法。我有一个特征,它的方法需要一些参数,并且为了具有灵活性,我希望能够以任何可能的组合将参数设为特定类型,只要传入类型的关联类型实现给定的特征.
注意 trait 的类型参数 A
和 B
表示函数的输入。现在我有一个版本,其中这些参数是函数的通用参数,但这不起作用,因为此 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
类型,而且A
和B
类型根本不会影响函数的输出,因为返回类型不依赖于这些类型参数。请注意,如果我添加更多类型参数,情况会变得更糟。我希望每个方法的每个参数都有一个类型参数,只是为了让我可以拥有响应所有可能的输入组合的函数。
哦,我想我明白了。是不是像下面这样:因为方法update没有使用B
类型,那么调用kf.update不知道使用哪个实现,因为它可能使用任何实现,因此它根本拒绝使用任何实现?
最后,如何解决这个问题?有没有办法说类型参数是完全独立的,或者至少说关联类型只依赖于第一个类型参数?
老兄,我是单身人士的一个懒惰初始化者。我在看你生锈的答案,一个真诚的问题:值得学习吗?您真诚的意见。【参考方案2】:
编辑:还有一件事,如果我放弃将类型组合作为输入的能力,并且如果我在上面的特征中删除类型参数 B,它就会起作用。不知何故,第二个类型参数的引入会导致问题。
假设您有两个具有相同T
和A
和不同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 时,“无法推断 `_` 的类型”