0Pricing
Learn Rust Coding · 课时

定义和实现 trait

掌握 trait,用于为不同类型定义共享行为,其作用类似于其他语言中的接口。

定义和实现 trait 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 2 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 3 节课。

Rust 中的 trait 是什么?

在 Rust 中,trait 是一种在不同类型之间定义共享行为的强大方式。您可以将它们理解为其他语言中的接口。

trait 会告诉 Rust,某个特定类型具备某些功能。如果某个类型实现了一个 trait,就表示该类型提供了该 trait 定义的方法。

  • trait 支持多态:以统一的方式处理不同类型。
  • trait 是 Rust 类型系统和安全抽象的关键组成部分。

声明您的第一个 trait

使用 trait 关键字定义 trait。在其中列出方法签名(名称、参数和返回类型),任何实现该 trait 的类型都必须提供这些方法。

让我们为可以生成摘要的项定义一个简单的 Summary trait。

trait Summary {
  fn summarize(&self) -> String;
}

fn main() {
  // Traits are definitions, not directly executable.
  // We will implement and use them in later scenes!
  println!("Trait 'Summary' defined.");
}

为类型实现 trait

要让类型使用某个 trait,请使用 impl Trait for Type 语法。然后,为 trait 中定义的每个方法提供具体实现。

这里,我们为 NewsArticle 结构体实现 Summary。

trait Summary {
  fn summarize(&self) -> String;
}

struct NewsArticle {
  headline: String,
  location: String,
  author: String,
  content: String,
}

impl Summary for NewsArticle {
  fn summarize(&self) -> String {
    format!("{}, by {} ({})", self.headline, self.author, self.location)
  }
}

fn main() {
  let article = NewsArticle {
    headline: String::from("Penguins win Stanley Cup!"),
    location: String::from("Pittsburgh, PA"),
    author: String::from("Iceburgh"),
    content: String::from("The Pittsburgh Penguins have won..."),
  };

  println!("New article summary: {}", article.summarize());
}

使用 trait bound 函数

类型实现 trait 后,您就可以编写接受任何实现该 trait 的类型的函数。这让代码更加灵活且具有泛型特性。

notify 函数可以接受任何实现了 Summary 的类型。

trait Summary {
  fn summarize(&self) -> String;
}

struct NewsArticle {
  headline: String,
  location: String,
  author: String,
  content: String,
}

impl Summary for NewsArticle {
  fn summarize(&self) -> String {
    format!("{}, by {} ({})", self.headline, self.author, self.location)
  }
}

struct Tweet {
  username: String,
  content: String,
  reply: bool,
  retweet: bool,
}

impl Summary for Tweet {
  fn summarize(&self) -> String {
    format!("{}: {}", self.username, self.content)
  }
}

fn notify(item: &impl Summary) { // impl Summary is syntax sugar for a trait bound
  println!("Breaking news! {}", item.summarize());
}

fn main() {
  let tweet = Tweet {
    username: String::from("horse_ebooks"),
    content: String::from("of course, as you probably already know, people"),
    reply: false,
    retweet: false,
  };

  let article = NewsArticle {
    headline: String::from("Rust is awesome!"),
    location: String::from("Internet"),
    author: String::from("Rustacean"),
    content: String::from("Rust's type system is amazing."),
  };

  notify(&tweet);
  notify(&article);
}

带有默认行为的 trait

trait 还可以为其中的方法提供默认实现。这意味着,如果提供了默认实现,类型就不必实现每个方法。

类型可以选择使用默认实现,也可以使用自己的逻辑覆盖它。

trait Summary {
  fn summarize_author(&self) -> String; // New required method
  fn summarize(&self) -> String { // Default implementation
    format!("(Read more from {})", self.summarize_author())
  }
}

struct Tweet {
  username: String,
  content: String,
  reply: bool,
  retweet: bool,
}

impl Summary for Tweet {
  fn summarize_author(&self) -> String { // Must implement required method
    format!("@{}", self.username)
  }
  // We are using the default summarize() method here!
}

fn main() {
  let tweet = Tweet {
    username: String::from("dog_lover"),
    content: String::from("My dog is the best!"),
    reply: false,
    retweet: false,
  };

  println!("Tweet summary: {}", tweet.summarize());
}

结合 trait 的泛型函数

编写泛型函数或结构体时,可以使用 trait bound 指定泛型类型参数 T 必须实现某个 trait。

这样可以确保 T 能够使用该 trait 中的方法。

trait Displayable {
  fn display(&self);
}

struct Point<T> {
  x: T,
  y: T,
}

// Implement Displayable for Point<i32>
impl Displayable for Point<i32> {
  fn display(&self) {
    println!("Point: ({}, {})", self.x, self.y);
  }
}

// Generic function that works for any type T that implements Displayable
fn print_item<T: Displayable>(item: T) {
  item.display();
}

fn main() {
  let p = Point { x: 10, y: 20 };
  print_item(p);
  // This function only works for types that implement Displayable
  // let s = String::from("Hello");
  // print_item(s); // This would cause a compile-time error
}

组合多个 trait bound

有时,泛型类型需要实现多个 trait。您可以使用 + 运算符指定多个 trait bound。

对于更复杂的情况,尤其是包含许多泛型参数时,where 子句可以提升可读性。

use std::fmt::Debug;

trait Printable {
  fn print_info(&self);
}

struct Book {
  title: String,
  pages: u32,
}

impl Printable for Book {
  fn print_info(&self) {
    println!("Book: '{}' ({} pages)", self.title, self.pages);
  }
}

impl Debug for Book { // Book also implements Debug trait
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "Book {{ title: {}, pages: {} }}", self.title, self.pages)
  }
}

// A function that requires its argument to be both Printable and Debug
fn process_item<T: Printable + Debug>(item: T) {
  item.print_info();
  println!("Debug info: {:?}", item);
}

fn main() {
  let my_book = Book {
    title: String::from("The Rust Book"),
    pages: 600,
  };

  process_item(my_book);
}

使用 `impl Trait` 返回类型

impl Trait 语法不仅可用于函数参数,也可用于返回位置。当您希望返回一个实现了特定特征的类型,但又不想公开其确切的具体类型时,这非常有用。

它可以简化函数签名,并让您的接口保持灵活。

trait Greeter {
  fn greet(&self) -> String;
}

struct FriendlyGreeter;
impl Greeter for FriendlyGreeter {
  fn greet(&self) -> String {
    String::from("Hello there!")
  }
}

struct FormalGreeter;
impl Greeter for FormalGreeter {
  fn greet(&self) -> String {
    String::from("Greetings and salutations.")
  }
}

// This function returns *some* type that implements Greeter
// The caller doesn't need to know if it's FriendlyGreeter or FormalGreeter
fn get_greeter(formal: bool) -> impl Greeter {
  if formal {
    FormalGreeter
  } else {
    FriendlyGreeter
  }
}

fn main() {
  let casual_greet = get_greeter(false);
  println!("{}", casual_greet.greet());

  let formal_greet = get_greeter(true);
  println!("{}", formal_greet.greet());
}

基于超特征构建

有时,一个特征在逻辑上可能依赖于另一个特征。Rust 允许您定义超特征,也就是说,一个特征可以要求实现它的任何类型还必须实现另一个特定特征。

这通过 trait SubTrait: SuperTrait 语法实现。它就像是在说:“如果您能完成 X,就必须也能完成 Y。”

trait Printable {
  fn print_content(&self);
}

// Debug is a supertrait of PrintableDebug
// Any type implementing PrintableDebug must also implement Debug
trait PrintableDebug: Printable + std::fmt::Debug {
  fn print_debug_and_content(&self) {
    self.print_content();
    println!("Debug info: {:?}", self);
  }
}

struct Item {
  id: u32,
  name: String,
}

impl Printable for Item {
  fn print_content(&self) {
    println!("Item ID: {}, Name: {}", self.id, self.name);
  }
}

impl std::fmt::Debug for Item {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "Item {{ id: {}, name: {} }}", self.id, self.name)
  }
}

// Now we can implement PrintableDebug because Item implements both Printable and Debug
impl PrintableDebug for Item {}

fn main() {
  let my_item = Item {
    id: 101,
    name: String::from("Widget A"),
  };
  my_item.print_debug_and_content();
}

测试您的特征知识

请考虑下面的 Rust 代码片段。以下关于特征和特征约束的说法中,哪一项是 TRUE?

特征:您的行为工具箱

我们已经探索了 Rust 强大的特征系统!

  • 特征定义一组类型可以实现的方法,作用类似于接口。
  • 您可以使用 impl Trait for Type 为特定类型实现特征。
  • 特征约束(T: Trait)允许泛型函数和结构体对任何实现了所需特征的类型进行操作。
  • 特征可以拥有默认实现,您还可以指定多个特征约束(T: Trait1 + Trait2)或使用超特征。
  • impl Trait 还可用于返回位置,以隐藏具体类型。

特征是编写灵活、可复用且安全的 Rust 代码的基础!

常见问题解答

「定义和实现 trait」课时是免费的吗?

是的 — 「定义和实现 trait」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 3 节课。

「定义和实现 trait」这节课中我会学到什么?

掌握 trait,用于为不同类型定义共享行为,其作用类似于其他语言中的接口。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Learn Rust Coding 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 3 节。

「定义和实现 trait」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Learn Rust Coding 课中编写并运行代码吗?

能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 在 Rust 中编写泛型代码
  2. 定义和实现 trait
  3. 高级 trait 用法:关联类型
← 返回 Learn Rust Coding