问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Rust 程序设计语言学习——泛型、Trait和生命周期

创作时间:
2025-03-20 03:49:53
作者:
@小白创作中心

Rust 程序设计语言学习——泛型、Trait和生命周期

引用
CSDN
1.
https://blog.csdn.net/tyyj90/article/details/140032029

Rust是一门系统级编程语言,以其内存安全和并发安全著称。在Rust中,泛型、Trait和生命周期是三个核心概念,它们共同帮助开发者编写更安全、更灵活的代码。本文将详细介绍这些概念的基本原理、使用方法和实际应用,通过具体的代码示例帮助读者理解。

一、泛型

泛型是Rust中处理重复概念的重要工具。通过泛型,我们可以为函数签名或结构体创建定义,使其能够用于多种不同的具体数据类型。

1.1 在函数定义中使用泛型

在函数签名中使用泛型,可以使得代码更具适应性和复用性。例如:

fn add_impl<T>(num1: T, num2: T) -> T
where
    T: std::ops::Add<Output = T> + Copy,
{
    num1 + num2
}
fn main() {
    let a = 3.0f32;
    let b = 4.5f32;
    let ret = add_impl(a, b);
    println!("The result of {} + {} is {}", a, b, ret);
    let a1 = 3;
    let b1 = 4;
    let ret1 = add_impl(a1, b1);
    println!("The result of {} + {} is {}", a1, b1, ret1);
}

运行结果

The result of 3 + 4.5 is 7.5
The result of 3 + 4 is 7

1.2 结构体、方法定义中的泛型

同样可以在结构体中使用泛型,以创建更灵活的数据结构。例如:

struct Box<T> {
    width: T,
    height: T,
}
impl<T> Box<T> {
    fn area(&self) -> T
    where
        T: std::ops::Mul<Output = T> + Copy,
    {
        self.width * self.height
    }
}
impl<T> Box<T> {
    fn new(width: T, height: T) -> Self {
        Box { width, height }
    }
}
fn main() {
    let int_box = Box::new(10, 20);
    println!("The area of the integer box is: {}", int_box.area());
    let float_box = Box::new(10.5, 20.3);
    println!("The area of the float box is: {}", float_box.area());
}

运行结果

The area of the integer box is: 200
The area of the float box is: 213.15

1.3 枚举定义中的泛型

枚举也可以包含泛型数据类型。例如标准库中的Option<T>枚举:

enum Option<T> {
    Some(T),
    None,
}

1.4 泛型代码的性能

Rust通过编译时的单态化(monomorphization)过程来优化泛型代码的性能。例如:

let integer = Some(5);
let float = Some(5.0);

编译器会生成类似以下的单态化代码:

enum Option_i32 {
    Some(i32),
    None,
}
enum Option_f64 {
    Some(f64),
    None,
}
fn main() {
    let integer = Option_i32::Some(5);
    let float = Option_f64::Some(5.0);
}

二、Trait

Trait类似于其他语言中的接口,用于定义类型的行为。例如:

pub trait Animal {
    fn speak(&self);
    fn name(&self) -> &str;
}
struct Dog {
    name: String,
}
impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
    fn name(&self) -> &str {
        &self.name
    }
}
struct Cat {
    name: String,
}
impl Animal for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
    fn name(&self) -> &str {
        &self.name
    }
}
fn animal_sound<T: Animal>(animal: &T) {
    println!("{} says: ", animal.name());
    animal.speak();
}
fn main() {
    let dog = Dog {
        name: "Rex".to_string(),
    };
    let cat = Cat {
        name: "Whiskers".to_string(),
    };
    animal_sound(&dog);
    animal_sound(&cat);
}

运行结果

Rex says: 
Woof!
Whiskers says: 
Meow!

三、生命周期

生命周期是Rust中确保引用安全的重要机制。例如:

fn main() {
    let a;
    {
        let b = 1;
        a = &b;
        println!("a: {a} b: {b}");
    }
    println!("a: {a}");
}

编译报错信息:

Compiling playground v0.0.1 (/playground)
error[E0597]: `b` does not live long enough
  --> src/main.rs:6:13
   |
5  |         let b = 1;
   |             - binding `b` declared here
6  |         a = &b;
   |             ^^ borrowed value does not live long enough
7  |         println!("a: {a} b: {b}");
8  |     }
   |     - `b` dropped here while still borrowed
9  |
10 |     println!("a: {a}");
   |                  --- borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` (bin "playground") due to 1 previous error

3.1 生命周期避免了悬垂引用

生命周期的主要目标是避免悬垂引用。例如:

fn main() {
    let a;                           //---------+-- 'a
                                     //         |
    {                                //         |
        let b = 1;                   //-+-- 'b  |
        a = &b;                      // |       |
        println!("a: {a} b: {b}");   // |       |
    }                                //-+       |
                                     //         |
    println!("a: {a}");              //         |
}                                    //---------+

3.2 函数中的泛型生命周期

例如:

fn max<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
    if (*x) > (*y) {
        x
    } else {
        y
    }
}
fn main() {
    let a = 10;
    let b = 20;
    println!("max: {}", max(&a, &b));
}

运行结果

max: 20

3.3 函数签名中的生命周期注解

生命周期注解的语法:

&i32        // 引用
&'a i32     // 带有显式生命周期的引用
&'a mut i32 // 带有显式生命周期的可变引用

3.4 结构体和方法定义中的生命周期注解

例如:

struct Message<'a> {
    content: &'a str,
}
impl<'a> Message<'a> {
    fn print(&self) {
        println!("The message is: {}", self.content);
    }
}
fn main() {
    let text = "Hello, Rust!".to_string();
    let message = Message { content: &text };
    message.print();
}

运行结果

The message is: Hello, Rust!

3.5 生命周期省略

Rust编译器采用三条规则来判断引用何时不需要明确的注解:

  1. 每个引用参数都分配一个生命周期参数。
  2. 如果只有一个输入生命周期参数,它被赋予所有输出生命周期参数。
  3. 如果方法有多个输入生命周期参数并且其中一个参数是&self&mut self,则所有输出生命周期参数被赋予self的生命周期。

3.6 静态生命周期

例如:

let s: &'static str = "I have a static lifetime.";

3.7 结合泛型类型参数、trait bounds 和生命周期

例如:

trait Append {
    fn append(&mut self, other: &str);
}
impl Append for String {
    fn append(&mut self, other: &str) {
        self.push_str(other);
    }
}
struct Appender<'a, T: Append> {
    item: &'a mut T,
}
impl<'a, T: Append> Appender<'a, T> {
    fn add_content(&mut self, other: &str) {
        self.item.append(other);
    }
}
fn main() {
    let mut text = String::from("Hello, ");
    let mut appender = Appender { item: &mut text };
    appender.add_content("world!");
    println!("{}", text);
}

运行结果

Hello, world!

参考链接

  1. Rust 官方网站:https://www.rust-lang.org/zh-CN
  2. Rust 官方文档:https://doc.rust-lang.org/
  3. Rust Play:https://play.rust-lang.org/
  4. 《Rust 程序设计语言》
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号