【问题标题】:Why is `impl` needed when passing traits as function parameters?为什么将特征作为函数参数传递时需要`impl`?
【发布时间】:2019-08-20 23:35:51
【问题描述】:

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

我知道 trait 是更通用的类型而不是具体的类型,但是由于 Rust 编译器不允许跨结构和 trait 共享名称,为什么需要在函数签名中提供 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());
}

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

【问题讨论】:

  • 我知道 trait 是更通用的类型,而不是具体的类型 — trait 的名称 又是一个类型和一个 trait。见What does “dyn” mean in a type?
  • 看来What does impl mean when used as the argument type or return type of a function? 的答案可能会回答您的问题。如果没有,请edit您的问题来解释差异。否则,我们可以将此问题标记为已回答。
  • 你只是在问为什么是impl Foo而不是trait Foo?语法 impl Foo 可以理解为“实现 Foo”,因此语法非常合理。
  • 是的@mcarton。这都是关于语法的。在 go 和 java 等其他语言中,接口也是一种类型,您将参数的类型命名为 interface 。我希望 rustc 有类似的东西有 f: trait Foo 而不是 f: impl Foo 或只是 f: Foo 因为编译器无论如何都不允许重复类型。

标签: syntax rust traits


【解决方案1】:

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

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

静态调度不是 Go 或 Java 所具备的。在 C++ 中,它适用于模板和鸭子类型。在 Rust 中,它适用于泛型和特征,其原始语法是:

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

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

fn some_function(foo: impl Trait) { … }

相当于上面的。

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

fn some_function() -> impl Trait { … }

这意味着some_function 可以返回任何实现Trait 的单一类型,但此类型必须在编译时已知。例如,这比返回 Box&lt;Trait&gt; 具有一些性能优势。在 C++ 中,最接近的等价物将返回 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。这很好理解为“实现 Foo 的类型”。 original RFC 没有过多讨论替代语法。 Another RFC discusses the syntax more,特别是语法是否应该在参数位置是any Foo,在返回位置是some Foo。据我所知,trait Foo 的语法从未被考虑过。

【讨论】:

  • 非常清晰的解释,特别是特征的无大小特性,如果与大小具体的类型混合,可能会造成混淆。我认为这type Foo = impl Bar; 会很有意义。非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-28
  • 2011-06-25
  • 2023-03-25
  • 1970-01-01
相关资源
最近更新 更多