为什么将特征作为函数参数传递时需要 impl ?

Why is `impl` needed when passing traits as function parameters?

在下面传递一个特征作为参数的例子中,函数签名中发送impl的必要性是什么?

我知道 traits 是更通用的类型而不是具体类型,但是由于 Rust 编译器不允许跨结构和 traits 共享名称,为什么需要在函数签名中提供 impl代表类型?

pub fn notify(item: impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

文档提到上面的签名只是下面签名的语法糖。使用 trait Summary 而不是 impl Summary 是否有意义,因为 impl 也可用于定义结构上的方法?

pub fn notify<T: Summary>(item: T) {
    println!("Breaking news! {}", item.summarize());
}

是否有我遗漏的隐藏概念?

与 Go 或 Java 等语言相反,Rust 允许静态和动态调度,并且需要一些语法让程序员在两者之间进行选择。

由于动态分派必须对可能不是 Sized 的对象起作用,您需要引用才能使用它。也就是说,您将使用 &dyn TraitBox<dyn Trait>(注意:由于历史原因,dyn 关键字不是必需的,但现代 Rust 使用它)。在 C++ 中,动态调度也需要引用或指针。

Go 或 Java 没有静态调度。在 C++ 中,它与模板和鸭子类型一起使用。在 Rust 中,它适用于泛型和特征,其原始语法为:

fn some_function<T: Trait>(foo: T) { … }

后来,语言中添加了以下语法:

fn some_function(foo: impl Trait) { … }

相当于上面的

此语法最初是为了在 return 类型中使用而发明的,其中没有通用的等效项:

fn some_function() -> impl Trait { … }

这意味着 some_function 可以 return 实现 Trait 的任何单个类型,但必须在编译时知道该类型。例如,这比 returning Box<Trait> 有一些性能优势。在 C++ 中,最接近的等价物是 returning autodecltype(auto).

为对称添加了参数位置的语法。

您可能想知道为什么不简单地使泛型隐式并具有:

fn some_function(foo: Trait) { … }

但这会有点令人困惑。 Trait 本身没有大小,因此不能用作参数,除非它们是通用的。这将使特征在未确定大小的类型领域中脱颖而出。例如,如果 (foo: Trait) 有效,您可能想知道为什么 (foo: str) 无效,但那是什么意思?使泛型隐式化还有其他问题,例如,特征中的泛型使特征非对象安全。


稍后,Rust 可能会扩展这些存在类型并在模块级别允许这样做:

type Foo = impl Bar;

(目前允许在夜间播放,由 type_alias_impl_trait 功能保护)


最后,您问的是为什么语法是 impl Foo,而不是 trait Foo。这读起来很好 "a type that implements Foo"。 original RFC doesn't discuss alternative syntaxes much. Another RFC discusses the syntax more,特别是语法是否应该在参数位置any Foo,以及return位置some Foo。据我所知,语法 trait Foo 从未被考虑过。