原文链接:https://dev.to/brunooliveira/rust-understanding-traits-1-45md

原文标题:Rust - understanding traits 1

公众号:Rust 碎碎念


概述(Introduction)

如今很多语言都是面向对象,支持这种范式,或者不是直接面向对象,而是允许程序员定义自己的数据类型。

通常,我们这些类型在特定条件下表现出特定的​​​trait​​​,例如,当向标准输出打印类型的时候,或者可能要遍历一个特定类型时,我们需要知道如何执行这些操作。这些通过保证我们的类型实现了特定的方法(methods)来定义,通常这些方法由一个 ​​trait​​ 来提供。

Traits

一个 trait 是针对未知类型(即 Self)定义的方法的一个集合。它们可以访问同一 trait 中声明的其他方法。

可以为任意数据类型实现 trait。在下面的例子中,我们定义 Animal(译者注:即动物),一组方法:

trait Animal {
// Static method signature; `Self` refers to the implementor type.
fn new(name: &'static str) -> Self;

// Instance method signatures; these will return a string.
fn name(&self) -> &'static str;
fn noise(&self) -> &'static str;

// Traits can provide default method definitions.
fn talk(&self) {
println!("{} says {}", self.name(), self.noise());
}
}

定义这个 trait 之后,我们现在可以在自己的类型上实现这个 trait,这意味着它们将会有一个 animal 的所有行为,而且仍然可以添加它们自己的功能。

作为 animals 的示例,我们可以定义两个完全不同的 animal 类型:一个​​​StuffedAnimal(毛绒玩具动物)​​​和一个​​Cat(猫)​​。它们都是 animals,因此,它们共享特定的共同的特征,比如都有名字,交流以及产生噪音,但是无论是毛绒玩具动物(stuffed animal)还是一只真正的猫,它们执行这些行为的方式是不一样的。让我们看一下,我们怎样使我们的类型实现一个特定的 trait。

使一个自定义类型实现一个 trait(Making a custom type implement a trait)

我们首先定义两个非常基本的自定义类型,这两个类型表示我们想要去阐述的概念,一个毛绒玩具动物和一只猫:

struct Cat {
name: &'static str,
age: i32
}

struct StuffedAnimal {
name: &'static str
}

一只猫可以通过一个名字和它的年龄来简单地进行表示,而一个毛绒玩具动物仅简单地有一个名字。

现在,这些类型可以被实例化并像下面这样来使用:

fn main() {
let c=Cat{name: "Bobi",age:8};
println!("Cat's name is {} and he is {} years old",c.name, c.age);
}

因此,我们可以利用这些表示我们的类型的属性,在代码中对特定概念进行更高级别的表示。

有了 trait,这个想法就可以扩展开来,因为在本质上,我们的类型将要表现出由他们实现的 trait 所定义的行为。在我们的具体案例中,我们将能够把我们的类型看作 animals,因为,它们将能够和指定的声音交谈。使用我们上面看到的​​​new()​​​方法对它们进行实例化也是可以的,该方法简单地引用我们在上面看到的“手工制作的(hand-made)”结构体创建。

下面是如何为一个类型实现一个 trait:

impl Animal for Cat {
// `Self` is the implementor type: `Cat`.
fn new(name: &'static str) -> Cat {
Cat { name: name, age: 1 }
}

fn name(&self) -> &'static str {
self.name
}

fn noise(&self) -> &'static str {
"Meowww"
}

// Default trait methods can be overridden.
fn talk(&self) {
// For example, we can add some quiet contemplation.
println!("{} pauses briefly... {}", self.name, self.noise());
}
}

这里使用的语法:​​impl <trait name> for <type name>​​作为头部声明,像许多其他的语言一样,在定义和真正的 trait 实现中,我们根据底层实现来定义我们想要的方法的实现。正如我们所见,一只猫发出声音时是 meows(译者注:类似于喵~)。

这里需要注意的另一个重要事情是,我们可以覆写(override)默认的 trait 方法,在这个特殊的例子中,我们为猫的具体情况覆写了​​talk()​​方法。

所以,这就是我们如何为一个用户定义的类型定义一个 trait。

完整例子(Full example)

作为一个完整例子,我们还可以为一个毛绒玩具动物实现这个 trait:

trait Animal {
// Static method signature; `Self` refers to the implementor type.
fn new(name: &'static str) -> Self;

// Instance method signatures; these will return a string.
fn name(&self) -> &'static str;
fn noise(&self) -> &'static str;

// Traits can provide default method definitions.
fn talk(&self) {
println!("{} says {}", self.name(), self.noise());
}
}

struct Cat {
name: &'static str,
age: i32
}

struct StuffedAnimal {
name: &'static str
}

impl Animal for Cat {
fn new(name: &'static str) -> Cat {
Cat { name: name, age: 1 }
}

fn name(&self) -> &'static str {
self.name
}

fn noise(&self) -> &'static str {
"Meowww"
}

// Default trait methods can be overridden.
fn talk(&self) {
// For example, we can add some quiet contemplation.
println!("{} pauses briefly... {}", self.name, self.noise());
}
}

impl Animal for StuffedAnimal {
fn new(name: &'static str) -> StuffedAnimal {
StuffedAnimal { name: name}
}

fn name(&self) -> &'static str {
self.name
}

fn noise(&self) -> &'static str {
"<random factory noise>"
}
}

fn main() {
let c: Cat=Animal::new("Bobi");
println!("Cat's name is {} {} ",c.name(), c.age);
c.talk();

let stuffed: StuffedAnimal=Animal::new("BobiStuffed");
stuffed.talk();
}

所以,我们可以看到,当我们运行这段代码时,根据 trait 的不同实现,我们可以得到不同的行为。还要注意一下,第二个例子中的 talk 方法返回默认的实现。

总结(Conclusion)

在对 trait 进行基础的介绍之后,你应该能够为你自己的类型添加自定义的 trait 实现,并使你的类型更加灵活且更接近于你的问题域!请继续关注更多关于 Rust 的内容!

【译】Rust——理解trait (一)_ide