初面网初面网

impl

impl 用来给类型(结构体、枚举、trait 等)绑定方法。

基本用法

struct Dog {
    name: String,
    age: u8,
}

impl Dog {
    fn bark(&self) {
        println!("{} 汪汪!", self.name);
    }
}

fn main() {
    let my_dog = Dog {
        name: String::from("旺财"),
        age: 3,
    };
    my_dog.bark();  // 输出:旺财 汪汪!
}

关键点:

  • impl Dog 表示"给 Dog 这个类型写方法"
  • &self 表示"这个方法需要读取自己"(类似 Python 的 self)
  • 调用时用 my_dog.bark()

关联函数 vs 实例方法

impl 里可以写两种东西:

  • 实例方法(带 &self):必须有一个具体的实例才能调用,用 . 调用。
  • 关联函数(不带 &self):相当于构造函数或工具函数,用 类型名::函数名() 调用。
impl Dog {
    // 关联函数:用来创建新狗(类似 Java 的静态方法)
    fn new(name: &str, age: u8) -> Dog {
        Dog {
            name: String::from(name),
            age: age,
        }
    }

    // 实例方法:需要具体的一只狗
    fn birthday(&mut self) {
        self.age += 1;
        println!("{} 过生日啦,现在 {} 岁", self.name, self.age);
    }
}

fn main() {
    // 用 :: 调用关联函数
    let mut my_dog = Dog::new("小黑", 2);

    // 用 . 调用实例方法
    my_dog.birthday(); // 小黑过生日啦,现在 3 岁
}

口诀:

  • &self → 实例方法 → 用 . 调用
  • 没 &self → 关联函数 → 用 :: 调用

self 的三种形式

写法含义能做什么
&self只读借用只能读,不能改
&mut self可变借用可以修改自身
self取得所有权用完就销毁(移动)
impl Dog {
    fn read(&self) {
        println!("名字是 {}", self.name); // 只读
    }

    fn change(&mut self) {
        self.age += 1; // 可以改
    }

    fn destroy(self) {
        // 拿到所有权,方法结束后 drop,狗就没了
        println!("{} 被销毁了", self.name);
    }
}

为外部类型实现方法(孤儿规则)

可以为外部类型实现方法,但有个限制:类型或 trait 至少有一个是在本地定义的。

trait MyTrait {
    fn shout(&self);
}

impl MyTrait for String {
    fn shout(&self) {
        println!("{}!!!", self);
    }
}

fn main() {
    let s = String::from("hello");
    s.shout(); // hello!!!
}

这其实就是 trait 的实现。

泛型实现

struct Pair<T> {
    first: T,
    second: T,
}

// 为所有 T 实现方法
impl<T> Pair<T> {
    fn new(first: T, second: T) -> Self {
        Pair { first, second }
    }

    fn first(&self) -> &T {
        &self.first
    }
}

// 只针对 T = i32 额外实现一个方法
impl Pair<i32> {
    fn sum(&self) -> i32 {
        self.first + self.second
    }
}

fn main() {
    let p1 = Pair::new(10, 20);
    println!("{}", p1.sum()); // 30,因为 T 是 i32

    let p2 = Pair::new("a", "b");
    // p2.sum(); // 编译错误!字符串没有 sum
}

impl Trait for Type

为类型实现某个 trait(接口),这是最核心的用法之一。

// 定义一个 trait(类似 Java 的 interface)
trait Speak {
    fn speak(&self);
}

// 为 Dog 实现 Speak
impl Speak for Dog {
    fn speak(&self) {
        println!("{} 说:汪汪", self.name);
    }
}

// 为 Cat 实现 Speak
struct Cat { name: String }
impl Speak for Cat {
    fn speak(&self) {
        println!("{} 说:喵喵", self.name);
    }
}

// 写一个通用函数,接受任何会 Speak 的东西
fn make_it_speak<T: Speak>(item: &T) {
    item.speak();
}

fn main() {
    let dog = Dog::new("旺财", 3);
    let cat = Cat { name: String::from("咪咪") };

    make_it_speak(&dog); // 旺财 说:汪汪
    make_it_speak(&cat); // 咪咪 说:喵喵
}

多个 impl 块

可以把方法拆成多个 impl 块,编译时会合并:

impl Dog {
    fn bark(&self) { ... }
}

impl Dog {
    fn walk(&self) { ... }
}

// 完全合法,逻辑上分组而已

实际中常用来:

  • 一个块放通用方法
  • 另一个块放针对特定 trait 的实现

where 约束

当泛型约束很多时,可以用 where 让代码更清晰:

use std::fmt::Display;

struct Wrapper<T> { value: T }

// 不用 where(挤在一行)
impl<T: Display + Clone> Wrapper<T> {
    fn show(&self) { println!("{}", self.value); }
}

// 用 where(更清晰)
impl<T> Wrapper<T>
where
    T: Display + Clone,
{
    fn show_clone(&self) -> T {
        println!("{}", self.value);
        self.value.clone()
    }
}

返回 impl Trait

可以用 impl Trait 作为返回类型,不暴露具体类型,只说"返回一个实现了某 trait 的东西":

fn create_dog() -> impl Speak {
    Dog::new("旺财", 3) // 只返回 Dog,没问题
}

注意:impl Trait 只能返回单一具体类型。若要根据条件返回不同具体类型(如 Dog 和 Cat),需使用 Box<dyn Speak> 这类 trait 对象。

总结

场景写法举例
给结构体加方法impl StructName { fn ... }impl Dog { fn bark(&self) }
关联函数(无 self)fn new() -> SelfDog::new()
实例方法(读)&selffn name(&self) -> &String
实例方法(改)&mut selffn grow(&mut self)
实现 traitimpl Trait for Typeimpl Speak for Dog
泛型实现impl<T> Type<T>impl<T> Pair<T>
返回不透明类型-> impl Traitfn make() -> impl Speak

练习

写一个 Rectangle 结构体,包含宽和高:

  1. 用 impl 实现一个关联函数 new(width, height)
  2. 实现实例方法 area() 计算面积
  3. 实现 can_hold(&self, other: &Rectangle) 判断是否能容纳另一个矩形
  4. 为 Rectangle 实现 Debug trait(用 #[derive(Debug)] 最简单)

更新于 2026/8/17