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() -> Self | Dog::new() |
| 实例方法(读) | &self | fn name(&self) -> &String |
| 实例方法(改) | &mut self | fn grow(&mut self) |
| 实现 trait | impl Trait for Type | impl Speak for Dog |
| 泛型实现 | impl<T> Type<T> | impl<T> Pair<T> |
| 返回不透明类型 | -> impl Trait | fn make() -> impl Speak |
练习
写一个 Rectangle 结构体,包含宽和高:
- 用
impl实现一个关联函数new(width, height) - 实现实例方法
area()计算面积 - 实现
can_hold(&self, other: &Rectangle)判断是否能容纳另一个矩形 - 为
Rectangle实现Debugtrait(用#[derive(Debug)]最简单)