» Rust快速入门 » 2. 高级篇 » 2.4 Traits 特征

Traits 特征

特征是为未知类型定义的一组方法的集合。

// 定义一个名为 `Greet` 的特征
trait Greet {
    // 一个名为 `greet` 的方法
    fn greet(&self);
}

// 为 `Person` 结构实现 `Greet` 特征
struct Person {
    name: String,
}

impl Greet for Person {
    // 为 `Person` 结构实现 `greet` 方法
    fn greet(&self) {
        println!("Hello, my name is {}!", self.name);
    }
}

fn main() {
    let person = Person {
        name: String::from("Alice"),
    };

    // 在 `person` 实例上调用 `greet` 方法
    person.greet();
}

Derive (获得)

编译器能够通过 #[derive] 属性为一些特征提供基本实现。

以下是可获得的一些特征列表:

  • 比较特征:Eq, PartialEq, Ord, PartialOrd.
  • Clone,用于从 &T 创建 T
  • Copy,提供类型的“复制语义”而不是“移动语义”。
  • Hash,用于从 &T 计算哈希值。
  • Default,创建数据类型的空实例。
  • Debug,使用 {:?} 格式化值。
#[derive(PartialEq, PartialOrd)]
struct Duration(f64);

#[derive(Debug)]
struct S(i32);

返回特征

Rust 编译器需要知道每个函数的返回类型需要多少空间。这意味着所有的函数都必须返回一个具体的类型。

Rust 在堆上分配内存时尽量明确。因此,如果函数返回一个指向堆上的特征的指针,你需要用 dyn 关键字写出返回类型,例如 Box<dyn Drawable>

trait Drawable {
    fn draw(&self);
}

struct Circle {
    radius: f64,
}

impl Drawable for Circle {
    fn draw(&self) {
        println!("Drawing a circle with radius {}.", self.radius);
    }
}

struct Square {
    side_length: f64,
}

impl Drawable for Square {
    fn draw(&self) {
        println!("Drawing a square with side length {}.", self.side_length);
    }
}

fn get_shape(is_circle: bool) -> Box<dyn Drawable> {
    if is_circle {
        Box::new(Circle { radius: 5.0 })
    } else {
        Box::new(Square { side_length: 8.0 })
    }
}

fn main() {
    let shape1 = get_shape(true);
    let shape2 = get_shape(false);

    shape1.draw();
    shape2.draw();
}

运算符重载

在 Rust 中,许多运算符可以通过特征进行重载。

use std::ops::Add;

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

fn main() {
    let point1 = Point { x: 1, y: 2 };
    let point2 = Point { x: 3, y: 4 };
    let result = point1 + point2;
    println!("Result: {:?}", result);
    // Result: Point { x: 4, y: 6 }
}

Drop

Drop 特征只有一个方法:drop,它在对象超出作用域时自动调用。 Drop 特征的主要用途是释放实现者实例所拥有的资源。

struct CustomResource {
    data: String,
}

impl Drop for CustomResource {
    fn drop(&mut self) {
        println!("Dropping CustomResource with data: {}", self.data);
    }
}

fn main() {
    let resource = CustomResource {
        data: String::from("learning on literank.com"),
    };

    println!("Using the CustomResource");

    // 在此块的末尾,`resource` 将超出作用域,
    // 并自动调用 `drop` 方法。
}

迭代器

Iterator 特征用于在集合(如数组)上实现迭代器。

struct Counter {
    current: usize,
    end: usize,
}

impl Iterator for Counter {
    type Item = usize;

    // 定义 `next` 方法,它返回序列中的下一个值
    fn next(&mut self) -> Option<Self::Item> {
        if self.current < self.end {
            let result = Some(self.current);
            self.current += 1;
            result
        } else {
            None
        }
    }
}

fn main() {
    let counter = Counter { current: 0, end: 5 };

    // 使用 `for` 循环与迭代器一起遍历值
    for num in counter {
        println!("Count: {}", num);
    }

    // 你还可以使用 `Iterator` 特征提供的其他方法
    let squares: Vec<usize> = Counter { current: 0, end: 5 }
        .map(|x| x * x)
        .collect();

    println!("Squares: {:?}", squares);
}

impl Trait

impl Trait 可以在两种情况下使用:

  • 作为参数类型
  • 作为返回类型

作为参数类型

trait Printer {
    fn print(&self);
}

fn print_shape(shape: impl Printer) {
    shape.print();
}

作为返回类型

trait Shape {
    fn area(&self) -> f64;
}

struct Circle {
    radius: f64,
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

fn create_shape() -> impl Shape {
  Circle { radius: 3.0 }
}

fn main() {
    let shape = create_shape();
    println!("Area: {}", shape.area());
}

Clone (克隆)

处理资源时,默认行为是将它们在赋值或函数调用期间传递。然而,有时你需要复制资源。

Clone 特征就用于此场景。

#[derive(Debug, Clone)]
struct Person {
    name: String,
    age: u32,
}

fn main() {
    let person1 = Person {
        name: String::from("Alice"),
        age: 20,
    };

    // 克隆 `Person` 实例以创建新实例
    let person2 = person1.clone();

    // 打印原始和克隆的人物
    println!("Original Person: {:?}", person1);
    // Original Person: Person { name: "Alice", age: 20 }

    println!("Cloned Person: {:?}", person2);
    // Cloned Person: Person { name: "Alice", age: 20 }
}

Super Trait(超特征)

Rust 没有"继承",但你可以将一个特征定义为另一个特征的"超特征"。

trait Printable {
    fn print(&self);
}

// 定义一个名为 `DebugPrintable` 的子特征,扩展了 `Printable`
trait DebugPrintable: Printable {
    fn debug_print(&self);
}

// 为 `Circle` 结构实现 `Printable` 特征
struct Circle {
    radius: f64,
}

impl Printable for Circle {
    fn print(&self) {
        println!("Printing a circle with radius {}", self.radius);
    }
}

// 为 `Circle` 结构实现 `DebugPrintable` 特征
impl DebugPrintable for Circle {
    fn debug_print(&self) {
        println!("Debug printing a circle with radius {}", self.radius);
    }
}

fn main() {
    // 创建 `Circle` 的实例
    let circle = Circle { radius: 3.0 };

    // 调用两个特征的方法
    circle.print(); // Printing a circle with radius 3
    circle.debug_print(); // Debug printing a circle with radius 3
}

代码挑战

尝试修改编辑器中提供的代码以使用特征进行操作。

Loading...
> 此处输出代码运行结果
上页
下页