C10 泛型,Trait,生命周期
- [10.1 提取函数以消除重复代码](#10.1 提取函数以消除重复代码)
-
- [10.1.1 例子](#10.1.1 例子)
- [10.1.2`疑问:为什么 if item > largest { 这里使用的是item而不是&item?`](#10.1.2
疑问:为什么 if item > largest { 这里使用的是item而不是&item?)
- [10.2 泛型](#10.2 泛型)
-
- [10.2.1 定义](#10.2.1 定义)
- [10.2.2 函数定义中的泛型](#10.2.2 函数定义中的泛型)
- [10.2.3 struct结构体中 定义的泛型](#10.2.3 struct结构体中 定义的泛型)
- [10.2.4 enum枚举类型中 ->主要让枚举的变体持有泛型数据](#10.2.4 enum枚举类型中 ->主要让枚举的变体持有泛型数据)
- [10.2.5 方法定义中的泛型](#10.2.5 方法定义中的泛型)
- [10.2.6 泛型代码的性能](#10.2.6 泛型代码的性能)
- [10.3 Trait](#10.3 Trait)
-
- [10.3.1 功能](#10.3.1 功能)
- [10.3.2 定义一个trait](#10.3.2 定义一个trait)
- [10.3.3 在类型上实现trait](#10.3.3 在类型上实现trait)
-
- [10.3.3.1 一般写法](#10.3.3.1 一般写法)
- [10.3.3.2 默认实现](#10.3.3.2 默认实现)
- [10.3.3.2 默认实现的方法可以调用trait中其他的方法](#10.3.3.2 默认实现的方法可以调用trait中其他的方法)
- [10.3.4 trait作为参数](#10.3.4 trait作为参数)
-
- [10.3.4.1 impl Trait 语法:适用于简单情况](#10.3.4.1 impl Trait 语法:适用于简单情况)
- [10.3.4.2 Trait bound 语法:适用于复杂情况](#10.3.4.2 Trait bound 语法:适用于复杂情况)
- [10.3.4.3 使用+指定多个Trait bound](#10.3.4.3 使用+指定多个Trait bound)
- [10.3.4.4 Trait bound 使用where子句](#10.3.4.4 Trait bound 使用where子句)
- [10.3.5 实现Trait作为返回类型](#10.3.5 实现Trait作为返回类型)
-
- [10.3.5.1 impl Trait 语法](#10.3.5.1 impl Trait 语法)
- [10.3.5.2 使用Trait Bound](#10.3.5.2 使用Trait Bound)
- [10.3.5.2 使用Trait Bound有条件的实现方法](#10.3.5.2 使用Trait Bound有条件的实现方法)
- [10.4 生命周期](#10.4 生命周期)
-
- [10.4.1 生命周期的简单定义](#10.4.1 生命周期的简单定义)
- [10.4.2 避免悬垂引用(dangling reference)](#10.4.2 避免悬垂引用(dangling reference))
- [10.4.3 函数中的泛型生命周期](#10.4.3 函数中的泛型生命周期)
- [10.4.4 生命周期标注语法](#10.4.4 生命周期标注语法)
- [10.4.5 函数签名中的生命周期标注](#10.4.5 函数签名中的生命周期标注)
- [10.4.6 深入理解生命周期](#10.4.6 深入理解生命周期)
- [10.4.7 struct定义中的生命周期标注](#10.4.7 struct定义中的生命周期标注)
- [10.4.8 生命周期的省略规则](#10.4.8 生命周期的省略规则)
- [10.4.9 结构体添加生命周期标注](#10.4.9 结构体添加生命周期标注)
- [10.4.10 静态生命周期](#10.4.10 静态生命周期)
10.1 提取函数以消除重复代码
10.1.1 例子
- code1: 实现找到一个数组中的最大值
rust
fn main()
{
let vec = vec![252, 20, 666,18,1];
let mut largest = vec[0];
for item in vec {
if item > largest {
largest = item;
}
}
println!("{}", largest);
let vec = vec![1000,0,12,5];
let mut largest = vec[0];
for item in vec {
if item > largest {
largest = item;
}
}
println!("{}", largest);
}
输出:
666
1000
- code2: 如果有多段相同的逻辑,需要提取相同的代码
- 用的切片
rust
fn main()
{
let vec = vec![252, 20, 666,18,1];
println!("{}", get_largest_num(&vec));
}
fn get_largest_num(vec: &[i32]) -> i32{
let mut largest = vec[0];
// 在这一行item的类型是&i32,所以&item的类型是i32
// 这里已经发生了解引用
for &item in vec {
if item > largest {
largest = item;
}
}
return largest;
}
10.1.2疑问:为什么 if item > largest { 这里使用的是item而不是&item?
-
1️⃣ 关键在这一行
for &item in vec {注意:这里已经发生了
解引用。 -
2️⃣ vec 的真实类型是什么?
rustfn get_largest_num(vec: &[i32]) -> i32vec的类型是:&[i32](切片)对切片进行
for x in vec时:rustfor x in vec { // x 的类型是 &i32 }默认遍历的是
引用,不是值。 -
3️⃣ 那为什么 item 是 i32?
因为你写的是:
for &item in vec {这是
模式解构(destructuring pattern)。等价于:
rustfor item_ref in vec { let item = *item_ref; }写法 item 的类型 for item in vec &i32 for &item in vec i32 👉 &item 是一个 模式,不是取地址
👉 它的意思是:
"我期望拿到一个 &i32,然后把它解引用成 i32,绑定到 item"
-
4️⃣ 所以这里为什么是 item 而不是 &item?
此时:
rustitem: i32 largest: i32所以你写:
rustif item > largest {是 值对值的比较,完全正确 ✅
如果你写成:
if &item > &largest {那就变成了:
&i32 > &i32虽然 Rust 也能比较(通过解引用),但没必要,也不直观。
-
5️⃣ 如果不用 &item,会发生什么?
**写法一(你现在用的,最推荐)**✅
rustfor &item in vec { if item > largest { largest = item; } }写法二(显式解引用)
rustfor item in vec { if *item > largest { largest = *item; } }对比 说明 for &item in vec 模式解构,简洁、惯用 *item 显式解引用,稍显啰嗦
10.2 泛型
10.2.1 定义
- 泛型:提高代码的
复用能力 - 类似使用一个模板充当"占位符"
- 编译时,"占位符"会被替换成具体的类型
- 例如:
fn largest<T>(list: &[T]) -> T { ... }
10.2.2 函数定义中的泛型
- 泛型函数
- 参数类型
- 返回类型
- 例子
rust
// 简单举例,这里的T需要有限制
fn get_largest_num<T>(vec: &[T]) -> T{
let mut largest = vec[0];
for &item in vec {
if item > largest {
largest = item;
}
}
return largest;
}
10.2.3 struct结构体中 定义的泛型
- 主要用于字段
- 例子
rust
struct Point<T>{
x: T,
y: T,
}
fn main(){
let point1 = Point{x:5, y:10};
let point2 = Point{x:0.1, y:10.0};
}
扩展:
rust
struct Point<T, U>{
x: T,
y: U,
}
fn main(){
let point1: Point<i32, i32> = Point{x:5, y:10};
let point2: Point<f64, i32> = Point{x:0.1, y:10};
}
10.2.4 enum枚举类型中 ->主要让枚举的变体持有泛型数据
- Option
<T>
rust
enum Option<T> {
Some(T),
None,
}
- Result
<T, E>
rust
enum Result<T,E> {
Ok(T),
Err(E),
}
10.2.5 方法定义中的泛型
-
为struct或者enum实现方法时,可以在定义中使用泛型:case1
- 注意:把T放在impl关键字后,表示在类型T上实现方法。例如:impl
<T> Point<T>
- 注意:把T放在impl关键字后,表示在类型T上实现方法。例如:impl
-
也可以为特定的类型实现方法(其余类型没有实现方法):case2:impl Point
<i32> -
例子
rust
struct Point<T>{
x: T,
y: T,
}
// case1
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// case2
impl Point<i32> {
fn x1(&self) -> &i32 {
&self.x
}
}
- struct 里的泛型类型参数可以和方法的泛型类型参数不同
rust
#[derive(Debug)]
struct Point<T, U>{
x: T,
y: U,
}
impl<T, U> Point<T, U> {
// <V, W>为输入参数的类型
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W>{
Point {
x:self.x,
y: other.y
}
}
}
fn main(){
let point1 = Point{x:5, y:10};
let point2 = Point{x:0.1, y:10.0};
let p3 = point1.mixup(point2);
println!("{:?}", p3);
}
输出:
rust
Point { x: 5, y: 10.0 }
10.2.6 泛型代码的性能
- 速度一样
- 单态化
- 在编译时将泛型替换为具体的类型
10.3 Trait
10.3.1 功能
- trait:抽象定义共享行为
- trait bounds(约束):为泛型类型T指定为实现了特定行为的类型(T : xxx)
- 类似其他语言的interface接口,但有些区别
10.3.2 定义一个trait
- 不同的类型有相同的方法,就可以将这个
相同的方法提取出来作为trait- 关键字:trait
- 只有方法的签名,没有具体实现
- trait可以有多个方法:每个方法签名占一行,以
;结尾 - 实现该trait的类型必须提供具体的方法实现
rust
pub trait Speak {
fn speak(&self) -> String;
}
10.3.3 在类型上实现trait
10.3.3.1 一般写法
- 与为类型实现方法类似
- 但是写法略有不同
- impl Speak for Dog {...}
- 其中Speak 是trait,Dog是struct
- 在impl块中,需要对trait里面的方法签名进行具体的实现
rust
use std::fmt::format;
struct Dog {
name: String,
voice: String,
color: i32,
}
struct Cat {
name: String,
voice: String,
hight: i32,
}
pub trait Speak {
fn speak(&self) -> String;
}
impl Speak for Dog {
fn speak(&self) -> String {
format!("{}. {}", self.name, self.voice)
}
}
fn main(){
let dog1 = Dog {
name: String::from("dog1"),
voice: String::from("Wangwang..."),
color: 128
};
let voice = dog1.speak();
println!("{}", voice);
}
输出:
rust
dog1. Wangwang...
10.3.3.2 默认实现
rust
pub trait Speak {
fn speak(&self) -> String{
String::from("Make moice")
}
}
- 例子
rust
use std::fmt::format;
struct Dog {
name: String,
voice: String,
color: i32,
}
struct Cat {
name: String,
voice: String,
hight: i32,
}
pub trait Speak {
fn speak(&self) -> String{
String::from("Make moice")
}
}
// 在这里也可以不实现Speak fn
impl Speak for Cat {}
fn main(){
let cat1 = Cat {
name: String::from("cat1"),
voice: String::from("Wangwang..."),
hight: 12
};
let voice1 = cat1.speak();
println!("{}", voice1);
}
输出:
rust
Make moice
10.3.3.2 默认实现的方法可以调用trait中其他的方法
- 默认实现的方法可以调用trait中其他的方法,即使这些方法没有默认实现
rust
pub trait Speak {
fn sayhi(&self) -> String;
// 这里speak调用了sayhi,尽管sayhi还没有实现
fn speak(&self) -> String{
self.sayhi()
}
}
- 例子
rust
use std::fmt::format;
struct Dog {
name: String,
voice: String,
color: i32,
}
struct Cat {
name: String,
voice: String,
hight: i32,
}
pub trait Speak {
fn sayhi(&self) -> String;
// 这里speak调用了sayhi,尽管sayhi还没有实现
fn speak(&self) -> String{
self.sayhi()
}
}
impl Speak for Dog {
fn sayhi(&self) -> String {
format!("{}, Hi", self.name)
}
fn speak(&self) -> String {
format!("{}. {}", self.name, self.voice)
}
}
impl Speak for Cat {
fn sayhi(&self) -> String {
format!("{}, Hi", self.name)
}
}
fn main(){
let dog1 = Dog {
name: String::from("dog1"),
voice: String::from("Wangwang..."),
color: 128
};
let voice = dog1.speak();
println!("{}", voice);
let cat1 = Cat {
name: String::from("cat1"),
voice: String::from("Wangwang..."),
hight: 12
};
let voice1 = cat1.speak();
println!("{}", voice1);
}
输出:
rust
dog1. Wangwang...
cat1, Hi
- 注意:无法从方法的重写实现里面调用默认的实现
10.3.4 trait作为参数
10.3.4.1 impl Trait 语法:适用于简单情况
impl Trait 语法:适用于简单情况
函数参数item: impl Speak表明这个函数的传入参数是所有已经实现了Speak的struct
rust
fn animal_speak(item: impl Speak)
{
println!("{}", item.speak());
}
- 例子
rust
use std::fmt::format;
struct Dog {
name: String,
voice: String,
color: i32,
}
struct Cat {
name: String,
voice: String,
hight: i32,
}
pub trait Speak {
fn sayhi(&self) -> String;
fn speak(&self) -> String{
self.sayhi()
}
}
impl Speak for Dog {
fn sayhi(&self) -> String {
format!("{}, Hi", self.name)
}
fn speak(&self) -> String {
format!("{}. {}", self.name, self.voice)
}
}
impl Speak for Cat {
fn sayhi(&self) -> String {
format!("{}, Hi", self.name)
}
}
fn animal_speak(item: impl Speak)
{
println!("{}", item.speak());
}
fn main(){
let dog1 = Dog {
name: String::from("dog1"),
voice: String::from("Wangwang..."),
color: 128
};
animal_speak(dog1);
}
10.3.4.2 Trait bound 语法:适用于复杂情况
Trait bound 语法:适用于复杂情况
- impl Trait语法是Trait bound语法糖
rust
fn animal_speak<T: Speak>(item: T)
{
println!("{}", item.speak());
}
10.3.4.3 使用+指定多个Trait bound
使用+指定多个Trait bound
- 对于impl Trait
rust
fn animal_speak(item: impl Speak + Hold)
{
println!("{}", item.speak());
}
- 对于Trait bound
rust
fn animal_speak<T : Speak + Hold>(item: T)
{
println!("{}", item.speak());
}
10.3.4.4 Trait bound 使用where子句
Trait bound 使用where子句,在方法签名后面指定where子句
原代码:
rust
pub fn notify<T: Summary + Display, U: Clone + Debug> (a: T, b: U) -> String {
xxxx
}
使用where的代码
rust
pub fn notify<T, U> (a: T, b: U) -> String
where
T : Summary + Display,
U: Clone + Debug,
{
xxxx
}
10.3.5 实现Trait作为返回类型
10.3.5.1 impl Trait 语法
impl Trait 语法
- 返回类型:
-> impl Speak
rust
fn animal_speak(s: &str) -> impl Speak
{
return Dog {
name: String::from(s),
voice: String::from("Wangwang..."),
color: 128
};
}
- 返回类型只能是实现了Trait 的某一个具体的类型比如说Dog,当可能会返回两个类型的时候就会报错
rust
fn animal_speak(flag: bool) -> impl Speak
{
if flag == true {
return Dog {
name: String::from(s),
voice: String::from("Wangwang..."),
color: 128
};
}
else {
return Cat {
name: String::from(s),
voice: String::from("Wangwang..."),
hight: 128
};
}
}
- 例子
rust
use std::fmt::{Display, format};
struct Dog {
name: String,
voice: String,
color: i32,
}
struct Cat {
name: String,
voice: String,
hight: i32,
}
pub trait Speak {
fn sayhi(&self) -> String;
fn speak(&self) -> String{
self.sayhi()
}
}
pub trait Hold {
fn sayhi(&self) -> String;
fn hold(&self) -> String{
self.sayhi()
}
}
impl Speak for Dog {
fn sayhi(&self) -> String {
format!("{}, Hi", self.name)
}
fn speak(&self) -> String {
format!("{}. {}", self.name, self.voice)
}
}
impl Speak for Cat {
fn sayhi(&self) -> String {
format!("{}, Hi", self.name)
}
}
fn animal_speak(s: &str) -> impl Speak
{
let dog1 = Dog {
name: String::from(s),
voice: String::from("Wangwang..."),
color: 128
};
return dog1;
}
fn main(){
let dog2 = animal_speak("Dog2");
println!("{}", dog2.sayhi());
}
10.3.5.2 使用Trait Bound
- 例子:使用Trait Bound修复largest的函数
rust
fn get_largest_num<T : PartialOrd + Copy>(vec: &[T]) -> T{
let mut largest = vec[0];
for &item in vec {
if item > largest { // std::cmp::PartialOrd
largest = item;
}
}
return largest;
}
rust
fn get_largest_num<T : PartialOrd + Clone>(vec: &[T]) -> &T{
let mut largest = &vec[0];
for item in vec {
if item > &largest { // std::cmp::PartialOrd
largest = &item;
}
}
return largest;
}
10.3.5.2 使用Trait Bound有条件的实现方法
- 转述下:我觉得就是对于某些trait Bound实现了特定的方法,而对于普通的没有实现该特殊方法
具体可以看例子:
rust
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 只有当T实现了Display + PartialOrd trait,才可以调用到cmp_display方法
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is x = {}", self.x);
}
}
}
fn main()
{
}
- 覆盖实现,对所有满足条件的类型T都实现
rust
// 为任何实现Display trait的类型,调用ToString 方法
impl<T: fmt::Display> ToString for T { ... }
10.4 生命周期
10.4.1 生命周期的简单定义
- rust的每个引用都有自己的生命周期
- 生命周期:引用保持有效的作用域
- 大多数情况:生命周期是隐式的、可以被推断的->有三个规则
- 当引用的生命周期可能以不同的方式相互关联时:需要手动标注生命周期
10.4.2 避免悬垂引用(dangling reference)
- 生命周期主要是为了避免悬垂引用
- 悬垂引用的例子

10.4.3 函数中的泛型生命周期
例子
rust
fn main() {
let string1 = String::from("abcdd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("{}", result);
}
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
编译会报错

修改:
rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
10.4.4 生命周期标注语法
-
生命周期的标注:描述多个引用的生命周期的关系,但不影响生命周期
-
生命周期的标注不会改变引用的生命周期的长度
-
当指定
泛型生命周期参数,函数可以接收带有任何生命周期的引用 -
语法
- 以'开头
- 通常全小写且非常短
- 很多人使用'a
-
位置
-
在引用的&符号后
-
使用空格将标注和引用类型分开
rustfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
-
-
对比
- &i32 // 一个引用
- &'a i32 //带有显式生命周期的引用
- &'a mut i32 // 带有显式生命周期的可变引用
单个生命周期标注本身没有意义
10.4.5 函数签名中的生命周期标注
- 标注在函数名和参数列表之间的<>里
rust fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { - 生命周期'a的实际生命周期是:x和y两个生命周期中较小的那个
10.4.6 深入理解生命周期
-
从函数返回引用时,返回类型的生命周期参数需要与其中一个参数的生命周期匹配
-
如果返回的引用没有指向任何参数,那边它只能引用函数内创建的值
- 会导致悬垂引用
下面的代码输出会报错
rustfn longest(x: &str, y: &str) -> &str { let res = String::from("aaa"); res.as_str() }修改方法:
rustfn longest(x: &str, y: &str) -> String { let res = String::from("aaa"); res } - 会导致悬垂引用
10.4.7 struct定义中的生命周期标注
- struct里可包括
- 自持有的类型
- 引用:需要在每个引用上添加生命周期标注
下面的code表明name的生命周期需要比Dog要长
rust
struct Dog<'a>{
name: &'a str,
}
10.4.8 生命周期的省略规则
- 典型例子
rust
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
- 生命周期省略规则不会提供完整的推断:
- 如果应用规则后,引用的生命周期仍然模糊不清 -> 编译错误
- 解决办法:添加生命周期标注
- 生命周期省略的三个规则
- 规则1应用于输入生命周期
- 规则2和3应用于输出生命周期
- 规则1:每个引用类型的参数都有自己的生命周期
- 规则2:如果只有1个输入生命周期参数,那么该生命周期被赋给所有的输出生命周期参数
- 规则3:如果有多个输入生命周期参数,但其中一个是&self或&mut self(是方法),那么self的生命周期会被赋给所有的输出生命周期参数
10.4.9 结构体添加生命周期标注
- 例子
rust
struct Dog<'a>{
name: &'a str,
}
impl<'a> Dog<'a>{
fn speak(&self) -> i32{
3
}
}
10.4.10 静态生命周期
- 'static 特殊的生命周期

2026/1/13