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

man*_*nth 6 syntax traits rust

在以下将特征作为参数传递的示例中,需要发送impl函数签名吗?

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

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

文档提到上面的签名只是下面签名的语法糖。trait Summary使用 as代替impl Summaryasimpl也可以用来定义结构体上的方法不是有意义吗?

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

我错过了它周围的任何隐藏概念吗?

mca*_*ton 18

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

\n\n

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

\n\n

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

\n\n
fn some_function<T: Trait>(foo: T) { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n\n

后来,以下语法被添加到该语言中:

\n\n
fn some_function(foo: impl Trait) { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n\n

这相当于上面的。

\n\n

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

\n\n
fn some_function() -> impl Trait { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n\n

这意味着some_function可以返回实现 的任何单一类型Trait,但该类型必须在编译时已知。Box<Trait>例如,与返回相比,这具有一些性能优势。在 C++ 中,最接近的等效项是返回autoor decltype(auto)

\n\n

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

\n\n

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

\n\n
fn some_function(foo: Trait) { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
\n\n

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

\n\n
fn some_function(foo: impl Trait) { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n\n

(目前每晚允许,由该type_alias_impl_trait功能保护)

\n\n
\n\n

最后,您问为什么语法是impl Foo,而不是trait Foo. 这读起来很好“一种实现 Foo 的类型”。原始RFC没有过多讨论替代语法。另一个 RFC 更多地讨论了语法,特别是语法是否应该位于any Foo参数位置和some Foo返回位置。trait Foo据我所知,从未考虑过语法。

\n