非标量转换:在进行泛型乘法时将“T”作为“f64”
Non-scalar cast: `T` as `f64` when doing generic multiplication
我想为泛型结构实现一个特征。特征块内的方法必须 return 非泛型类型。我在尝试投射时遇到问题:
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
吗?
Can I, not using unsafe blocks, cast T
to f64
?
不, 这有很多充分的理由。 T
可以是任何类型,所以它根本不需要与f64
兼容或相似。您对 T
的唯一了解是,它实现了 std::ops::Mul<Output=T>
。但这无济于事,因为该特征也没有说明 f64
。
所以您可以 做的是将 T
绑定为 std::ops::Mul<Output=f64>
。这意味着将两个 T
相乘将得到 f64
。然后你的函数就可以工作了(即使没有 as f64
转换),但是你使你的 impl 方式不那么通用。
解决这个问题的正确方法,是对多种数进行抽象。值得庆幸的是,num-traits
crate has already done that. In particular, you are probably interested in the 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, ...
然后为 <T as std::ops::Mul>::Output: AsF64
实现。
Can I, not using unsafe blocks, cast T
to f64
?
什么是 T
?将u8
转换为f64
的代码肯定与将u64
转换为f64
的代码不同(毕竟后者可能会失败)。
在我看来,你有两条路要走:
- 使
Area
通用于 T
,因此 return 成为 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()
}
}
我想为泛型结构实现一个特征。特征块内的方法必须 return 非泛型类型。我在尝试投射时遇到问题:
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
吗?
Can I, not using unsafe blocks, cast
T
tof64
?
不, 这有很多充分的理由。 T
可以是任何类型,所以它根本不需要与f64
兼容或相似。您对 T
的唯一了解是,它实现了 std::ops::Mul<Output=T>
。但这无济于事,因为该特征也没有说明 f64
。
所以您可以 做的是将 T
绑定为 std::ops::Mul<Output=f64>
。这意味着将两个 T
相乘将得到 f64
。然后你的函数就可以工作了(即使没有 as f64
转换),但是你使你的 impl 方式不那么通用。
解决这个问题的正确方法,是对多种数进行抽象。值得庆幸的是,num-traits
crate has already done that. In particular, you are probably interested in the 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, ...
然后为 <T as std::ops::Mul>::Output: AsF64
实现。
Can I, not using unsafe blocks, cast
T
tof64
?
什么是 T
?将u8
转换为f64
的代码肯定与将u64
转换为f64
的代码不同(毕竟后者可能会失败)。
在我看来,你有两条路要走:
- 使
Area
通用于T
,因此 return 成为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()
}
}