Arguments 是否有可能根据其参数计算Rust函数或trait方法的返回类型?
我能在《铁锈》中取得类似的成就吗?另见 更具体地说:是否可以根据函数或trait方法的参数计算其返回类型,并确保返回类型与其中一个参数的类型相同 考虑以下情况。我有两个结构:Arguments 是否有可能根据其参数计算Rust函数或trait方法的返回类型?,arguments,rust,type-promotion,Arguments,Rust,Type Promotion,我能在《铁锈》中取得类似的成就吗?另见 更具体地说:是否可以根据函数或trait方法的参数计算其返回类型,并确保返回类型与其中一个参数的类型相同 考虑以下情况。我有两个结构: #[derive(Debug, Clone, Copy)] struct MySimpleType(f64); #[derive(Debug, Clone, Copy)] struct MyComplexType(f64, f64); 其中,MySimpleType可以通过From特性升级为MyComplexType
#[derive(Debug, Clone, Copy)]
struct MySimpleType(f64);
#[derive(Debug, Clone, Copy)]
struct MyComplexType(f64, f64);
其中,MySimpleType
可以通过From
特性升级为MyComplexType
impl From<MySimpleType> for MyComplexType {
fn from(src: MySimpleType) -> MyComplexType {
let MySimpleType(x1) = src;
MyComplexType(x1, 0.0)
}
}
但是编译器不知道O
应该是S
或t
,我必须对大多数方法调用进行注释
我的第二次尝试是使用稍微不同的特性并编写两个实现:
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
}
}
第三次尝试类似于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
}
}
我认为实现提升的最简单方法是创建一个
Promote
特征:
trait Promote<Target> {
fn promote(self) -> Target;
}
impl<T> Promote<T> for T {
fn promote(self) -> T { self }
}
编译时不带类型提示,因为add(1u32,2u64)
只能是u64
,因此由于Vec
是同构集合,add(1u32,1u32)
必须在此处返回u64
然而,正如您所经历的,有时您需要能够将结果引导到类型推理所能处理的范围之外。没关系,你只需要另一个特质:
trait PromoteTarget {
type Output;
}
impl<T> PromoteTarget for (T, T) {
type Output = T;
}
这样,我们就可以重写baz
签名来正确地解释所有中间类型。不幸的是,我不知道如何在where
子句中引入别名,所以请做好准备:
fn baz<Result, A, B, C, D>(a: A, b: B, c: C, d: D) -> Result
where
A: Promote<<(A, B) as PromoteTarget>::Output>,
B: Promote<<(A, B) as PromoteTarget>::Output>,
C: Promote<<(C, D) as PromoteTarget>::Output>,
D: Promote<<(C, D) as PromoteTarget>::Output>,
(A, B): PromoteTarget,
(C, D): PromoteTarget,
<(A, B) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(A, B) as PromoteTarget>::Output>,
<(C, D) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(C, D) as PromoteTarget>::Output>,
Result: Add<Output = Result>
{
let lhs = foo(a, b).promote();
let rhs = bar(c, d).promote();
lhs + rhs
}
“直到需要一个具有n个参数的函数”——你是否考虑编写宏来根据需要生成必要的特征?我已经考虑使用宏,但我很好奇是否存在另一个解决方案。席,马蒂厄?您已经切换到where子句的块样式缩进了吗?
Promote
和@Shepmaster有什么区别:(1)我正在尝试习惯这种块样式缩进的东西,(2)我使用Promote
来演示这个概念,因为它更容易破解;它开始时有点不一样,老实说,我没意识到它现在正在将匹配到。我也不确定这正是OP想要的。。。或者说,类型推断是不够的。还有一个好处是,这个板条箱可以定义标准库类型的升级,尽管我猜
中的是为扩大整数转换而实现的(总是这样吗?)@Shepmaster:我想,From
的最让人恼火的地方是推广到usize
:它只在u8
中实现,以防你在8位体系结构上无所事事。因此,事实上,通过自定义特性,您有机会转换为usize
,以获得更多类型。。。但它确实是一个附带属性:)
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 add<Result, Left, Right>(left: Left, right: Right) -> Result
where
Left: Promote<Result>,
Right: Promote<Result>,
Result: Add<Output = Result>
{
left.promote() + right.promote()
}
fn main() {
let one: u32 = add(1u32, 1u32);
let two: u64 = add(1u32, 2u64);
let three: u64 = add(2u64, 1u32);
let four: u64 = add(2u64, 2u64);
println!("{} {} {} {}", one, two, three, four);
}
fn main() {
let v = vec![add(1u32, 1u32), add(1u32, 2u64)];
println!("{:?}", v);
}
trait PromoteTarget {
type Output;
}
impl<T> PromoteTarget for (T, T) {
type Output = T;
}
impl PromoteTarget for (u32, u64) {
type Output = u64;
}
impl PromoteTarget for (u64, u32) {
type Output = u64;
}
fn baz<Result, A, B, C, D>(a: A, b: B, c: C, d: D) -> Result
where
A: Promote<<(A, B) as PromoteTarget>::Output>,
B: Promote<<(A, B) as PromoteTarget>::Output>,
C: Promote<<(C, D) as PromoteTarget>::Output>,
D: Promote<<(C, D) as PromoteTarget>::Output>,
(A, B): PromoteTarget,
(C, D): PromoteTarget,
<(A, B) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(A, B) as PromoteTarget>::Output>,
<(C, D) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(C, D) as PromoteTarget>::Output>,
Result: Add<Output = Result>
{
let lhs = foo(a, b).promote();
let rhs = bar(c, d).promote();
lhs + rhs
}
============
Foo called
Left: u32
Right: u32
Result: u32
4