【发布时间】:2017-08-24 01:45:42
【问题描述】:
我可以在 Rust 中实现类似于 boost::math::tools::promote_args 的东西吗?另见Idiomatic C++11 type promotion
更具体地说:是否可以根据参数计算函数或特征方法的返回类型并确保返回类型与参数之一具有相同的类型?
考虑以下情况。我有两个结构:
#[derive(Debug, Clone, Copy)]
struct MySimpleType(f64);
#[derive(Debug, Clone, Copy)]
struct MyComplexType(f64, f64);
其中MySimpleType 可以通过From trait 提升为MyComplexType。
impl From<MySimpleType> for MyComplexType {
fn from(src: MySimpleType) -> MyComplexType {
let MySimpleType(x1) = src;
MyComplexType(x1, 0.0)
}
}
我想编写一个函数,它接受两个MySimpleType 或MyComplexType 类型的参数,如果所有参数都输入为MySimpleType,则返回一个MySimpleType 类型的值,否则该函数应该返回一个类型的值MyComplexType。假设我已经为这两种类型实现了Add<Output=Self>,我可以这样做:
trait Foo<S, T> {
fn foo(s: S, t: T) -> Self;
}
impl<S, T, O> Foo<S, T> for O
where O: From<S> + From<T> + Add<Output = Self>
{
fn foo(s: S, t: T) -> Self {
let s: O = From::from(s);
let t: O = From::from(t);
s + t
}
}
但是编译器不知道O 应该是S 或T,我必须注释大多数方法调用。
我的第二次尝试是使用稍微不同的 trait 并编写两个实现:
trait Foo<S, T> {
fn foo(s: S, t: T) -> Self;
}
impl Foo<MySimpleType, MySimpleType> for MySimpleType {
fn foo(s: MySimpleType, t: MySimpleType) -> Self {
s + t
}
}
impl<S, T> Foo<S, T> for MyComplexType
where MyComplexType: From<S> + From<T>
{
fn foo(s: S, t: T) -> Self {
let s: MyComplexType = From::from(s);
let t: MyComplexType = From::from(t);
s + t
}
}
但同样,编译器无法计算出的返回类型
Foo::foo(MySimpleType(1.0), MySimpleType(1.0))
第三次尝试类似于std::ops::{Add, Mul, ...}。使用关联类型并为每种可能的参数类型组合编写特定的实现
trait Foo<T> {
type Output;
fn foo(self, t: T) -> Self::Output;
}
impl<T: Add<Output=T>> Foo<T> for T {
type Output = Self;
fn foo(self, t: T) -> Self::Output {
self + t
}
}
impl Foo<MySimpleType> for MyComplexType {
type Output = Self;
fn foo(self, t: MySimpleType) -> Self::Output {
let t: Self = From::from(t);
self + t
}
}
impl Foo<MyComplexType> for MySimpleType {
type Output = MyComplexType;
fn foo(self, t: MyComplexType) -> Self::Output {
let s: MyComplexType = From::from(self);
s + t
}
}
这似乎是最好的解决方案,直到需要一个带有n 参数的函数。因为那时必须编写2^n - n + 1 impl 语句。当然,如果考虑超过两种类型,情况会变得更糟。
===
编辑:
在我的代码中,我有多个嵌套函数调用,我想避免不必要的类型提升,因为简单类型的函数评估对于复杂类型来说既便宜又昂贵。通过使用@MatthieuM。的建议的解决方案,这是没有实现的。请考虑以下示例
#![feature(core_intrinsics)]
use std::ops::Add;
trait Promote<Target> {
fn promote(self) -> Target;
}
impl<T> Promote<T> for T {
fn promote(self) -> T {
self
}
}
impl Promote<u64> for u32 {
fn promote(self) -> u64 {
self as u64
}
}
fn foo<Result, Left, Right>(left: Left, right: Right) -> Result
where Left: Promote<Result>,
Right: Promote<Result>,
Result: Add<Output = Result>
{
println!("============\nFoo called");
println!("Left: {}", unsafe { std::intrinsics::type_name::<Left>() });
println!("Right: {}",
unsafe { std::intrinsics::type_name::<Right>() });
println!("Result: {}",
unsafe { std::intrinsics::type_name::<Result>() });
left.promote() + right.promote()
}
fn bar<Result, Left, Right>(left: Left, right: Right) -> Result
where Left: Promote<Result>,
Right: Promote<Result>,
Result: Add<Output = Result>
{
left.promote() + right.promote()
}
fn baz<Result, A, B, C, D>(a: A, b: B, c: C, d: D) -> Result
where A: Promote<Result>,
B: Promote<Result>,
C: Promote<Result>,
D: Promote<Result>,
Result: Add<Output = Result>
{
let lhs = foo(a, b).promote();
let rhs = bar(c, d).promote();
lhs + rhs
}
fn main() {
let one = baz(1u32, 1u32, 1u64, 1u32);
println!("{}", one);
}
【问题讨论】:
-
“直到需要一个带有 n 个参数的函数” – 你是否考虑过编写一个宏来按需生成必要的特征?
-
我考虑过使用宏,但我很好奇是否存在其他解决方案。
标签: arguments rust type-promotion