Rust学习记录--C10 泛型,Trait,生命周期

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 的真实类型是什么?

    rust 复制代码
    fn get_largest_num(vec: &[i32]) -> i32

    vec 的类型是:&[i32](切片)

    对切片进行 for x in vec 时:

    rust 复制代码
    for x in vec {
        // x 的类型是 &i32
    }

    默认遍历的是引用,不是值。

  • 3️⃣ 那为什么 item 是 i32?

    因为你写的是:
    for &item in vec {

    这是 模式解构(destructuring pattern)

    等价于:

    rust 复制代码
    for 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?

    此时:

    rust 复制代码
    item: i32
    largest: i32

    所以你写:

    rust 复制代码
    if item > largest {

    是 值对值的比较,完全正确 ✅

    如果你写成: if &item > &largest {

    那就变成了: &i32 > &i32

    虽然 Rust 也能比较(通过解引用),但没必要,也不直观。

  • 5️⃣ 如果不用 &item,会发生什么?

    **写法一(你现在用的,最推荐)**✅

    rust 复制代码
    for &item in vec {
        if item > largest {
            largest = item;
        }
    }

    写法二(显式解引用)

    rust 复制代码
    for 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>
  • 也可以为特定的类型实现方法(其余类型没有实现方法):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
  • 位置

    • 在引用的&符号后

    • 使用空格将标注和引用类型分开

      rust 复制代码
      fn 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 深入理解生命周期

  • 从函数返回引用时,返回类型的生命周期参数需要与其中一个参数的生命周期匹配

  • 如果返回的引用没有指向任何参数,那边它只能引用函数内创建的值

    • 会导致悬垂引用
      下面的代码输出会报错
    rust 复制代码
    fn longest(x: &str, y: &str) -> &str {
     let res = String::from("aaa");
     res.as_str()
    }

    修改方法:

    rust 复制代码
    fn 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
相关推荐
寻星探路2 小时前
【算法进阶】滑动窗口与前缀和:从“和为 K”到“最小覆盖子串”的极限挑战
java·开发语言·c++·人工智能·python·算法·ai
WangYaolove13142 小时前
基于深度学习的身份证识别考勤系统(源码+文档)
python·mysql·django·毕业设计·源码
Moonquakes5402 小时前
嵌入式基础学习笔记(51)
笔记·单片机·学习
weixin_445054722 小时前
力扣热题53
开发语言·python
musenh2 小时前
spring学习1
java·学习·spring
数据大魔方2 小时前
【期货量化实战】豆粕期货量化交易策略(Python完整代码)
开发语言·数据库·python·算法·github·程序员创富
Engineer邓祥浩2 小时前
设计模式学习(12) 23-10 外观模式
学习·设计模式·外观模式
@汤圆酱2 小时前
【无标题】
python·jmeter
专注于大数据技术栈2 小时前
java学习--Vector
java·学习