非标量强制转换:执行泛型乘法时,`T`为`f64`

时间:2016-11-13 13:45:41

标签: casting rust traits

我想为泛型类型结构实现一个特性。特征块内的方法必须返回非泛型类型。我在尝试演员时遇到了问题:

struct Rectangle<T> {
    x: T,
    y: T,
    width: T,
    height: T,
}

trait Area {
    fn area(&self) -> f64;
}

impl<T> Area for Rectangle<T>
    where T: std::ops::Mul<Output=T> 
{
    fn area(&self) -> f64 {
        let t_area = self.height * self.width;
        let f_area = t_area as f64;
        f_area
    }
}

fn main() {
    let sq = Rectangle { x: 0, y: 0, height: 1, width: 1 };
    println!("{}", sq.area());
}

编译器输出为:

error: non-scalar cast: `T` as `f64`
  --> src/main.rs:22:22
   |
22 |         let f_area = t_area as f64;
   |                      ^^^^^^^^^^^^^

我可以在不使用不安全的阻止的情况下将T投射到f64吗?

2 个答案:

答案 0 :(得分:3)

  

我可以,不使用不安全的阻止,将T投射到f64吗?

不,并且有很多充分的理由。 T可以是任何类型,因此它不需要与f64 完全兼容或类似于。您对T唯一了解的是,它实现了std::ops::Mul<Output=T>。但这并没有帮助,因为这个特性也没有说明f64

因此可以做的是将T绑定为std::ops::Mul<Output=f64>。这意味着将两个T相乘会产生f64。然后你的函数可以工作(即使没有as f64强制转换),但是你的impl方式不那么通用了。

解决这个问题的正确方法是抽象出多种数字。值得庆幸的是,num-traits crate已经做到了。特别是,您可能对ToPrimitive trait感兴趣。那你的impl应该看起来有点像:

use num_traits::ToPrimitive;

impl<T> Area for Rectangle<T>
    where T: std::ops::Mul
          <T as std::ops::Mul>::Output: ToPrimitive

{
    fn area(&self) -> f64 {
        let t_area = self.height * self.width;
        t_area.to_f64().unwrap()
    }
}

小问题:我们这里有unwrap(),当乘法结果无法转换为f64时会发生恐慌。我不太确定为什么会发生这种情况,但我们需要注意这一点。如果没有这样的unwrap(),可能会有更好的解决方案。

您当然也可以创建自己的特质。类似的东西:

trait AsF64 {
    fn cast(self) -> f64;
}

impl AsF64 for i32 {
    fn cast(self) -> f64 { self as f64 }
}

// more impls for u32, u16, i16, ...

然后impl为<T as std::ops::Mul>::Output: AsF64

答案 1 :(得分:3)

  

我可以,不使用不安全的阻止,将T投射到f64吗?

T是什么?将u8转换为f64的代码肯定与将u64转换为f64的代码不同(毕竟后者可能会失败)。

在我看来,你前面有两条路:

  • 使Area通用超过T,从而返回T
  • T约束为可以转换为f64
  • 的类型

我将展示后者:

impl<T> Area for Rectangle<T>
    where T: std::ops::Mul<Output=T> + Clone + std::convert::Into<f64>
{
    fn area(&self) -> f64 {
        self.height.clone().into() * self.width.clone().into()
    }
}