我希望定义一个检查两个值并有选择地将其中一个更改为匹配类型的函数。目的是对数学公式执行自动转换。我的规则是:
i64
,别说了f64
,别说了i64
,一个是f64
,将i64
更改为f64
例如:
fn normalize(arg1: Option<MyValue>, arg2: Option<MyValue>)
-> (Option<MyValue>, Option<MyValue>) {
... do stuff...
}
我的示例将返回带有可选转换值的元组。 MyValue
未实现Copy
,但确实实现了Clone
。它是一个枚举,可以容纳整数,有理数或字符串等。
我可以看到的替代方法是:
None
。()
。设置参数&mut
。更改需要更改的内容。哪种方法最适合Rust?如果我不克隆,该如何标记签名以使借用检查器更安心?
我真正的枚举是:
#[derive(Clone, PartialEq, Debug)]
pub enum ShyScalar {
Boolean(bool),
Integer(i64),
Rational(f64),
String(String),
Error(String)
}
答案 0 :(得分:0)
您可以将具有关联类型的特征视为在类型之间进行映射的编译时函数。例如:
trait MapType {
type Output;
}
impl MapType for f64 {
type Output i64;
}
impl MapType for bool {
type Output u8;
}
对于您可能需要的每种类型,可以实现MapType
以提供到唯一Output
类型的映射。
您的情况是关于类型对的,您可以通过添加参数来扩展上面的想法:
trait Normalize<T>: Sized {
type Norm;
}
Normalize
的每个实现都会产生唯一的Norm
类型,并将Self
和T
两种类型组合在一起。
但是您还需要一些约束;毕竟,您将需要能够在这些类型之间进行实际转换。而且,如果数字太大而无法转换,则转换将失败,因此您将需要额外的类型约束TryFrom
和TryInto
,以表示可以将哪些内容转换为以下内容:
use std::convert::{TryFrom, TryInto};
trait Normalize<T>: Sized
where
T: TryInto<Self::Norm>,
{
type Norm: TryFrom<Self>;
}
为您想要的类型对实现它:
impl Normalize<u32> for f64 {
type Norm = f64;
}
impl Normalize<f64> for u32 {
type Norm = f64;
}
并且对于所有成对的 类型:
impl<X> Normalize<X> for X {
type Norm = X;
}
然后您可以像这样实现normalize
:
fn normalize<A, B>(arg1: Option<A>, arg2: Option<B>) -> (Option<A::Norm>, Option<A::Norm>)
where
A: Normalize<B>,
A::Norm: TryFrom<B>,
{
(
arg1.and_then(|a| a.try_into().ok()),
arg2.and_then(|b| b.try_into().ok())
)
}
fn main() {
println!("{:?}", normalize(Some(1u32), Some(1u32))); // (Some(1), Some(1))
println!("{:?}", normalize(Some(1f64), Some(1u32))); // (Some(1.0), Some(1.0))
println!("{:?}", normalize(Some(1u32), Some(1f64))); // (Some(1.0), Some(1.0))
println!("{:?}", normalize(Some(1f64), Some(1f64))); // (Some(1.0), Some(1.0))
}
通过使用u32
而不是u64
,您会很快意识到我在这里有点作弊。这是因为there is no TryFrom<u64>
implementation for f64
。如果您需要这些类型对,则仍然可以使用我概述的方法,但是您需要定义自己的TryInto
和TryFrom
特征版本,并为所有输入所需的配对。