首页 > 解决方案 > 如何在使用 `num::Float` 特征并与 Rust 中的原始类型交互时最小化样板的数量

问题描述

num::Float在使用trait 并与 Rust 中的原始类型交互时,是否有一种好的方法可以最小化样板代码的数量?例如,考虑一个写得不好的二次方程求解器

// External libraries
use num::Float;

// Poorly written quadratic formula solver for a x^2 + bx + c
fn myquad <Real> (a : Real, b : Real, c : Real) -> Option<(Real,Real)>
where
    Real : Float
{
    let mysqrt = Real::sqrt(b.powi(2)-Real::from(4.0)?*a*c);
    let r1 = (-b+mysqrt)/(Real::from(2.0)?*a);
    let r2 = (-b-mysqrt)/(Real::from(2.0)?*a);
    Some((r1,r2))
}

// Write a couple of tests
fn main() {
    let r1 = myquad::<f32> (1.0,1.0,-6.0).unwrap();
    println!("Roots of (x-2) (x+3):  ({},{})",r1.0,r1.1);
    let r2 = myquad::<f64> (6.0,5.0,-4.0).unwrap();
    println!("Roots of (2x-1) (3x+4):  ({},{})",r2.0,r2.1);
}

我希望该例程适用于andmyquad之外的各种浮点类型,但也适用于它们。也就是说,有一组重复的包装形式,其中 x 是原始浮点类型。虽然我理解类型一致性的必要性,但这有点冗长,我担心这些包装器对于具有大量原语的更复杂例程的可管理性。有没有更好的方法来处理这些转换或让它们隐式工作?可以肯定的是,答案可能是否定的,但我想在处理更复杂的例程之前了解这个成本。f32f64Real::from(x)?

标签: rust

解决方案


您遇到此障碍的原因是因为您期望num::Float成为一个已实施的特征。它不是。它的目的是作为扩展 trait

它为两者都实现,f32并且f64允许您使用它在这些类型上实现的所有方法,而无需在类型本身中实现它们。

然而,这并不意味着您可以神奇地添加一个T: Float界限并摆脱困境,因为您的运算需要乘法和减法。因此,您的常量(正如您自己发现的那样)需要实现Sub<X>and Mul<X>X您为常量选择的类型在哪里。

然而,有一个窍门。如果您知道常量的类型......您可以要求From<X>(其中 X 是常量的类型)。这意味着您可以以要求浮动大小的下限为代价,轻松解决这个问题。

在您的情况下,此下限要求不是问题,因为您依赖于powi声明的方法num::Float,并且此特征仅针对两种原始类型实现:f32f64. 如果你曾经想使用,比如说,half::f16你需要摆脱对powi. 因此,要求f32作为下限是完全可以接受的。

fn myquad<T:Float + From<f32>>(a : T, b: T, c: T) -> Option<(T, T)>
{
    let mysqrt = (b.powi(2) - a * c * (4.0.into())).sqrt();
    let r1 = (-b+mysqrt)/(a * 2.0.into());
    let r2 = (-b-mysqrt)/(a * 2.0.into());
    Some((r1,r2))
}

我认为就样板文件而言,这几乎是你可以做到的。


推荐阅读