为什么要使用特征?

Why use traits?

在 Rust 程序中使用特征的典型场景是什么?我已经尝试过思考它们,但对于为什么要决定使用它们,我仍然没有一个清晰的概念。我通过阅读 here.

了解语法

如果他们是 5 岁,如何向他们解释?

来自docs:

A trait is a collection of methods defined for an unknown type: Self. They can access other methods declared in the same trait.

Traits 类似于 Java、C++ 等语言中的接口

复习示例真的很有帮助,例如 ToString and the Display 特征。

如果你这样写:

format!("x is {x}");

如果 x 实现 Display 接口,代码将仅工作(和编译)。

同样,这只有在 x 实现 Debug 接口时才有效:

format!("{x:?}");

撇开与格式化字符串相关的“魔法”不谈,应该清楚 x 不能只是 any 类型的实例。需要有一个特定的 实现 将给定类型转换为字符串。请注意,例如,u16 的实现与 IpAddr 的实现完全不同。但是,这两种类型都有一个共同的行为。因此,字符串格式化程序甚至不需要知道哪些类型实现了 DisplayDebug 特性。这不关它的事。它只关心所讨论的类型是否实现了给定的特征(如果没有,编译器根本不会接受该代码)。

将特征概念化的一种方法是将它们视为 'behaviors you want available to, and to operate over, a particular chunk of state'。所以,如果你有一些包含状态的结构,并且你想用它做一些事情,你会为它写一个特征。

主要有两种用法:

  • 您正在代码中处理一个结构,您希望该结构知道如何执行某些行为。您可以在结构上调用 trait-defined 行为。
  • 您想将结构传递给其他一些代码(您的或第三方的),这些代码可能对结构本身一无所知,但想在其上执行一些功能,相信结构知道什么在这些情况下要做。

在第一种情况下,它允许您执行以下操作:

struct Article {
  body: String
}

trait Saveable {
  fn save(&self) -> ();
}

impl Saveable for Article {
  fn save(&self) -> () {
    ...  // All the code you need to run to save the Article object
  }
} 

// A function called by your UX
fn handle_article_update(article: Article) -> () {
  ...
  article.save()  // Call the save functionality
}

不过,第二种情况可以说更有趣。假设您 - 或者更可能是第三方 - 具有如下定义的函数:

fn save_object(obj: Saveable) -> () {
  ...
  obj.save()
}

struct Person {
  name: String
}

impl Saveable for Person {
  fn save(&self) -> () {
    ... // Code needed to save a Person object, could be different from that needed for an Article object
  }
}

...
// Note that we are using the same function to save both of these, despite being different underlying Structs
save_object(article)
save_object(person)

这意味着 save_object 函数不需要知道关于您的自定义 Article 结构的任何信息即可调用 save。为此,它可以简单地引用您对该方法的实现。通过这种方式,您可以编写第三方或通用库函数能够执行的自定义对象,因为 Trait 已经定义了一组具有代码可以安全依赖的契约的行为。

那么 'when do you want to use a Trait' 的问题可以这样回答:每当您想使用在结构上定义的行为而不需要了解有关该结构的所有信息时 - 只要它具有该行为即可。因此,在上面,您可能还会将 'edit' 功能附加到 Article,但不会附加到 Person。您不希望必须更改 save_object 来解决这个问题,甚至不关心。所有 save_object 需要知道的是 Saveable 特征中定义的函数已实现 - 它不需要知道有关对象的任何其他信息以同样有效地发挥作用。

另一种表达方式是,'Use a trait when you want to pass an object based on what it can do, not what it is.'