rust - 如何在使用 `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 是原始浮点类型。虽然我理解类型一致性的必要性,但这有点冗长,我担心这些包装器对于具有大量原语的更复杂例程的可管理性。有没有更好的方法来处理这些转换或让它们隐式工作?可以肯定的是,答案可能是否定的,但我想在处理更复杂的例程之前了解这个成本。f32
f64
Real::from(x)?
解决方案
您遇到此障碍的原因是因为您期望num::Float
成为一个已实施的特征。它不是。它的目的是作为扩展 trait。
它为两者都实现,f32
并且f64
允许您使用它在这些类型上实现的所有方法,而无需在类型本身中实现它们。
然而,这并不意味着您可以神奇地添加一个T: Float
界限并摆脱困境,因为您的运算需要乘法和减法。因此,您的常量(正如您自己发现的那样)需要实现Sub<X>
and Mul<X>
,X
您为常量选择的类型在哪里。
然而,有一个窍门。如果您知道常量的类型......您可以要求From<X>
(其中 X 是常量的类型)。这意味着您可以以要求浮动大小的下限为代价,轻松解决这个问题。
在您的情况下,此下限要求不是问题,因为您依赖于powi
声明的方法num::Float
,并且此特征仅针对两种原始类型实现:f32
和f64
. 如果你曾经想使用,比如说,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))
}
我认为就样板文件而言,这几乎是你可以做到的。