问题描述
在下面的将trait作为参数传递的例子中,在函数签名中发送impl
有什么需要?
In the following example of passing a trait as a parameter, what's the need of sending impl
in the function signature?
我知道 trait 是更通用的类型而不是具体类型,但是由于 Rust 编译器不允许跨结构和特征共享名称,为什么需要在函数中提供 impl
签名来表示类型?
I understand that traits are more generic types and not concrete types, but since the Rust compiler doesn't allow sharing names across structs and traits, why is there a need to provide impl
in the function signature to represent the type?
pub fn notify(item: impl Summary) {
println!("Breaking news! {}", item.summarize());
}
文档提到上面的签名只是下面签名的语法糖.使用 trait Summary
代替 impl Summary
难道没有意义,因为 impl
也可以用来定义结构体的方法吗?
The documentation mentions that the above signature is just syntactic sugar for the below signature. Wouldn't it make sense to use trait Summary
instead of impl Summary
as impl
can also be used to define methods on structs?
pub fn notify<T: Summary>(item: T) {
println!("Breaking news! {}", item.summarize());
}
是否有任何我遗漏的隐藏概念?
Is there any hidden concept around it that I'm missing?
推荐答案
与 Go 或 Java 等语言相反,Rust 允许静态和动态调度,并且需要一些语法让程序员在两者之间进行选择.
Contrary to languages such as Go or Java, Rust allows for both static and dynamic dispatch, and some syntax was required to let programmers choose between the two.
>
由于动态调度必须对可能不是 Sized
的对象起作用,因此您需要一个引用才能使用它.也就是说,您将使用 &dyn Trait
或 Box
(注意:由于历史原因,dyn
关键字不是必需的,但现代 Rust 使用它).在 C++ 中,动态调度也需要引用或指针.
As dynamic dispatch must work on objects which might not be Sized
, you need a reference to use it. That is, you would use &dyn Trait
or Box<dyn Trait>
(note: for historical reasons, the dyn
keyword is not required, but modern Rust uses it). In C++, dynamic dispatch also requires a reference or pointer.
静态调度不是 Go 或 Java 所拥有的.在 C++ 中,它适用于模板和鸭子类型.在 Rust 中,它适用于泛型和特征,其原始语法是:
Static dispatch is not something Go or Java have. In C++, it works with templates and duck-typing. In Rust, it works with generics and traits, and its original syntax was:
fn some_function<T: Trait>(foo: T) { … }
后来,在语言中添加了以下语法:
Later, the following syntax was added to the language:
fn some_function(foo: impl Trait) { … }
相当于上面的.
这个语法最初是为了在返回类型中使用而发明的,其中没有通用的等价物:
This syntax was originally invented to be used in return types, where there is no generic equivalent:
fn some_function() -> impl Trait { … }
这意味着 some_function
可以返回实现 Trait
的任何单个类型,但必须在编译时知道该类型.例如,这比返回 Box
有一些性能优势.在 C++ 中,最接近的等价物是返回 auto
或 decltype(auto)
.
This means that some_function
can return any single type that implements Trait
, but this type must be known at compile time. This has some performance benefits over returning Box<Trait>
for example. In C++, the closest equivalent would be returning auto
or decltype(auto)
.
为对称添加了参数位置的语法.
The syntax in parameter position was added for symmetry.
您可能想知道为什么不简单地使泛型隐式并具有:
You might wonder why not simply make the generics implicit and have:
fn some_function(foo: Trait) { … }
但这会有点令人困惑.Trait
本身没有大小,因此不能用作参数,除非它们是通用的.这将使特征在非大小类型的领域中脱颖而出.例如,如果 (foo: Trait)
可以工作,您可能想知道为什么 (foo: str)
不行,但这意味着什么?隐式泛型还存在其他问题,例如,traits 中的泛型使 trait 非对象安全.
But this would be slightly confusing. Trait
by itself is not sized, and therefore cannot be used as a parameter, unless they are generic. This would make traits stand out in the realm of unsized types. For example, if (foo: Trait)
would work, you might wonder why (foo: str)
doesn't, but what would that one mean? There is also other problems with making generics implicit, for example, generics in traits make the trait non-object-safe.
稍后,Rust 可能会扩展这些存在类型并在模块级别允许这样做:
Later, Rust will likely extend those existential types and allow this at a module level:
type Foo = impl Bar;
(目前每晚允许,由 type_alias_impl_trait
功能保护)
(which is currently allowed on nightly, guarded by the type_alias_impl_trait
feature)
最后,您要问为什么语法是 impl Foo
,而不是 trait Foo
.这很好读为实现 Foo 的类型".原始 RFC 没有过多讨论替代语法.另一个 RFC 更详细地讨论了语法,特别是语法是否应该在参数位置是any Foo
,在返回位置是some Foo
.据我所知,语法 trait Foo
从未被考虑过.
Finally, you are asking why the syntax is impl Foo
, rather than trait Foo
. This reads well as "a type that implements Foo". The original RFC doesn't discuss alternative syntaxes much. Another RFC discusses the syntax more, in particular whether the syntax should have been any Foo
in parameter position, and some Foo
in return position. The syntax trait Foo
was never considered as far as I am aware.
这篇关于为什么在将特征作为函数参数传递时需要 `impl`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!