如何为实现trait取别名?

10

我有很多以下类型签名的函数:

fn f() -> impl Fn(u32) -> u32 { 
    |x: u32| x 
}

我如何给Fn(u32) -> u32命名,以便不必重复使用它?虽然我可以做type X = Fn(u32) -> u32;,但Rust不允许我这样做,因为它是一种类型而不是特质。我必须等待 trait_alias ,还是我还可以做其他事情?

不太清楚——你是否有许多返回相同类型的函数实现了Fn(u32) -> u32,还是有许多返回不同类型(不同闭包)的函数都实现了Fn(u32) -> u32?我猜想是后者? - trent
1个回答

18
你说得很对。`impl X`要求`X`是一个trait,而在trait aliases出现之前,无法使用适当的trait别名。等到那时,你就可以这样做:
#![feature(trait_alias)]

trait X = Fn(u32) -> u32;

fn f() -> impl X {
    |x: u32| x
}

(游戏场)


另外,当在类型别名中允许impl Trait到来时,您将能够将impl trait作为类型别名。这有些不同。当我们使用type X = impl Trait进行别名处理时,编译器将确保每个X的使用实际上是相同的具体类型。这意味着,在您的情况下,您将无法将其与多个不同的闭包一起使用,因为每个闭包都有其自己独特的类型。

#![feature(type_alias_impl_trait)]

type X = impl Fn(u32) -> u32;

fn f() -> X {
    |x: u32| x
}

(游乐场)

然而,这个无法编译

#![feature(type_alias_impl_trait)]

type X = impl Fn(u32) -> u32;

fn f() -> X {
    |x: u32| x
}

// Even a closure with exactly the same form has a different type.
fn g() -> X {
    |x: u32| x
}

错误是{{error}}。
error: concrete type differs from previous defining opaque type use
  --> src/lib.rs:10:1
   |
10 | / fn g() -> X {
11 | |     |x: u32| x
12 | | }
   | |_^ expected `[closure@src/lib.rs:7:5: 7:15]`, got `[closure@src/lib.rs:11:5: 11:15]`
   |
note: previous use here
  --> src/lib.rs:6:1
   |
6  | / fn f() -> X {
7  | |     |x: u32| x
8  | | }
   | |_^

(游乐场)

这与特质别名形成对比,后者允许在每个返回impl TraitAlias的函数中使用不同的具体类型。有关介绍此语法存在类型的RFC,请参见更多信息。


在这两个功能中的任何一个落地之前,您可以使用一种类似于特质别名的方法来获得相似的行为。思路是创建一个新的特质,它本质上等同于原始特质,但名称更短。

// This trait is local to this crate,
// so we can implement it on any type we want.
trait ShortName: Fn(u32) -> u32 {}

// So let's go ahead and implement `ShortName`
// on any type that implements `Fn(u32) -> u32`.
impl<T: Fn(u32) -> u32> ShortName for T {}

// We can use `ShortName` to alias `Fn(u32) -> u32`.
fn f() -> impl ShortName {
    |x: u32| x
}

// Moreover, the result of that can be used in places
// that expect `Fn(u32) -> u32`.
fn g<T: Fn(u32) -> u32>(x: &T) -> u32 {
    x(6_u32)
}

fn main() {
    // We only know that `x` implements `ShortName`,
    let x = f();
    // But we can still use `x` in `g`,
    // which expects an `Fn(u32) -> u32` argument
    let _ = g(&x);
}

(游乐场)


将存在类型进行别名处理与将特质进行别名处理是非常不同的,这就是为什么我在问题评论中要求OP澄清。两者都不一定是错误的,但你的答案至少应该提到它们执行不同的操作。 - trent
@trentcl 我已经澄清了区别。感谢你让我知道。我以为它们是一样的,但显然不是。 - SCappella
我喜欢这个技巧。谢谢! - Listerone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接