在 Rust 中需要使用 f64 的泛型类型的标量乘法

Require scalar multiplication of a generic type with f64 in Rust

提问人:exocortex 提问时间:9/27/2022 最后编辑:exocortex 更新时间:9/27/2022 访问量:302

问:

我觉得我有两半的问题,不知道如何将它们组合在一起。 一方面,有“derive_more”板条箱,它允许自动派生特定类型的“添加”和“Mul”(+更多)特征。我已经成功地使用它为特定类型实现了以下函数,我自动为其派生了这些特征。

另一方面,我想编写一个通用函数,稍后将使用这些“Add”和“Mul”(+更多)特征。然而,它不起作用。(对于任何感兴趣的人,我想实现一个通用的 Runge-Kutta 函数来求解微分方程)。

pub fn update_rk4<T>(f: fn(&T) -> T, state: &mut T, dt: f64)
where
    T: Sized 
    + std::ops::Mul<Output = T> 
    + std::ops::Add<Output = T> 
    + std::ops::AddAssign,
{
    // runge kutta 4 method creates 4 "helper steps"
    let k1 = f(state);
    let k2 = f(&(*state + k1 * 0.5 * dt));
    let k3 = f(&(*state + k2 * 0.5 * dt));
    let k4 = f(&(*state + k3 * dt));

    *state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0 * dt);
}

我被困在这里。我收到很多这样的错误消息:


error[E0308]: mismatched types
 --> src/integrator.rs:7:32
  |
1 | pub fn update_rk4_with_f<T>(f: fn(&T) -> T, state: &mut T, dt: f64)
  |                          - this type parameter
...
7 |     let k2 = f(&(*state + k1 * 0.5 * dt));
  |                                ^^^ expected type parameter `T`, found floating-point number
  |
  = note: expected type parameter `T`
                       found type `{float}`

我在这里做错了什么?另外:我试图解决的问题的名称是什么?我想我也缺少一个好的搜索词来放入谷歌。

编辑:

在下面 Matthieu M. 的精彩回答的帮助下,我解决了我的问题。我尝试将“Mul<Output = T>”替换为“Mul<Rhs = f64,Output = T>”,但仍然出现错误。事实证明,这个想法是正确的,因为在特征实现中,“Rhs”默认为“T”,因此必须手动设置 - 但没有“Rhs”。解决方案很简单:“Mul<f64,输出 = T>”。然后,有了“复制”特征,一切都奏效了。最终的工作代码如下所示:

pub fn update_rk4<T>(f: fn(&T) -> T, state: &mut T, dt: f64)
where
    T: Sized + Copy
    + std::ops::Mul<f64, Output = T> 
    + std::ops::Add<T, Output = T> 
    + std::ops::AddAssign,
{
    // runge kutta 4 method creates 4 "helper steps"
    let k1 = f(state);
    let k2 = f(&(*state + k1 * 0.5 * dt));
    let k3 = f(&(*state + k2 * 0.5 * dt));
    let k4 = f(&(*state + k3 * dt));

    *state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0 * dt);
}
泛型 Rust 乘法

评论


答:

1赞 Matthieu M. 9/27/2022 #1

另外:我试图解决的问题的名称是什么?

我不知道任何具体的名字。

我在这里做错了什么?

更仔细地查看特征定义Add

pub trait Add<Rhs = Self> {
    type Output;

    fn add(self, rhs: Rhs) -> Self::Output;
}

有一个默认参数(键入时)表示右侧参数的类型:除非指定,否则此参数将为 。AddSelf

这正是错误消息告诉您的:

^^^ expected type parameter `T`, found floating-point number

您指定了 ,因此右手参数应该是 a 和 是文字,而不是 .T: Add<Output = Self>T0.5T


您可以通过以下几种方式之一解决此问题:

  1. 混合算术:实现 or for ,并指定为边界。Add<f32>Add<f64>TT: Add<fXX, Output = Self>
  2. 无误转换:在尝试操作之前添加补充绑定(或)并将文本转换为。T: From<f32>T: From<f64>T
  3. 自定义特征:使用您需要的常量创建一个新特征,并为您想要的所有特征实现该特征。T

虽然很吸引人,但如果混合算术是原语,它可能不是你想要的解决方案,因为你不能自己添加实现。T

如果任何(或)可以转换为 .使用将允许容易出错的转换,但也需要返回错误,这会恶化人体工程学。f32f64TTryFrom

后一种自定义特征方法更为严厉,但允许规避先前方法的局限性。

使用哪种方式取决于应该是什么。如果可能的话,我个人更喜欢 Infallible Conversion,因为它是一个轻量级的要求——不需要实现 5 或 10 个特征——或者如果转换不能是绝对可靠的,则使用自定义特征。T

评论

0赞 exocortex 9/27/2022
这在现在非常有帮助。只有一件事:你的意思是“Mul”而不是Add吗?因为就我而言,我只想将 T 相加(Output=T),但要求 Mul 也返回 T。
1赞 Matthieu M. 9/27/2022
@exocortex:所有数字特征都遵循相同的约定,我之所以使用,是因为它是第一个按字母顺序排列的,尽管在您的情况下确实似乎是问题所在。AddMul
0赞 exocortex 9/27/2022
哦,哇,谢谢!我刚刚解决了这个问题。我的实际错误是我试图使用它:“ ... + Mul<Rhs = f64,输出 = T> + ...".我实际上阅读了特征实现,但不明白“Rhs =”是要省略的。现在只需“Mul<f64,输出 = T>”和“Add<T,输出 = T> + Copy”,它实际上就可以工作了!哈哈!谢谢!(我是否也应该在这里对我自己的问题进行更长的回答?我觉得对我的锈蚀理解水平的更长的解释会很酷)
1赞 Matthieu M. 9/27/2022
@exocortex:当然可以;自从我成为初学者以来已经很久了,所以我有盲点。