2024 Rust现代实用教程 Borrowing借用 && Lifetime生命周期

文章目录

一、Borrowing && Borrow Checker && Lifetime

1.Borrowing

一个玩意两种描述

引用(Reference):

  • 1.引用是一种变量的别名,通过&符号来创建。(非所有权)
  • 2.引l用可以是不可变的(&T)或可变的(&mutT)。
  • 3.引用允许在不传递所有权的情况下访问数据,它们是安全且低开销的。

借用(Borrowing)

  • 1.借用是通过引l用(Reference)来借用(Borrow)数据,从而在一段时间内访问数据而不拥有它。
  • 2.借用分为可变借用和不可变借用。可变借用(&mut)允许修改数据,但在生命周期内只能有一个可变借用。不可变借用(&)允许多个同时存在。但不允许修改数据

2. Borrow Checker

·Borrow Checker的规则

1.不可变引用规则:

在任何给定的时间,要么有一个可变引用,要么有多个不可变引用,但不能同时存在可变引用和不可变引用。这确保了在同一时间只有一个地方对数据进行修改,或者有多个地方同时读取数据。

·2.可变引用规则:

在任何给定的时间,只能有一个可变引用来访问数据。这防止了并发修改相同数据的问题,从而防止数据竞争。生命周期规则:

3.引用的生命周期必须在被引用的数据有效的时间范围内。这防止了悬垂引用,即引用的数据已经被销毁,但引用仍然存在。

4.可变引用与不可变引用不互斥:

可以同时存在多个不可变引用,因为不可变引用不会修改数据,不会影响到其他引用。但不可变引用与可变引用之间是互斥的。

3.手动指定Lifetime

生命周期参数在函数/结构体签名中指定:

  • 一般情况下BorrowChecker会自行推断(随着Rust版本的更新,会越来越强)
  • 在函数/结构体签名中使用生命周期参数允许函数声明引用的有效范围。
rust 复制代码
fn main() {
    let mut s = String::from("Hello");
    // 不可变引用,可以同时有多个不可变引用
    let r1 = &s;
    let r2 = &s;
    println!("{} {}", r1, r2);

    let r3 = &mut s;
    println!("{}", r3);
    // println!("{} {}", r1, r2);//r1和r2已经失效了

    let result: &str;
    {
        // result = "ff";
        let r4 = &s;
        result = ff(r4);
    }
    // println!("r4 {}", r4);
    println!("{}", result);
}

// 手动指定Lifetime
fn ff<'a>(s: &'a str) -> &'a str {
    s
}

编译及运行

bash 复制代码
 cargo run
   Compiling ch20_borrow_check v0.1.0 (/home/wangji/installer/rust/project/ch20_borrow_check)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.85s
     Running `target/debug/ch20_borrow_check`
Hello Hello
Hello
Hello

二、Lifetime与函数

1.任何引用都是有生命周期的

大多数情况下,生命周期是隐式且被推断的

生命周期的主要目的是防止悬垂引用

关于"悬垂引用"的概念是指,引用指向的数据在代码结束后被释放,但引用仍然存在。

  • 生命周期的引入有助于确保引用的有效性,防止程序在运行时出现悬垂引用的情况。通过生命周期的推断,Rust 能够在编译时检查代码,确保引l用的有效性,而不是在运行时出现悬垂引用的错误。

2.生命周期与函数

编译器在没有显式注解的情况下,使用三个规则来推断这些生命周期:

  • 1.第一个规则是每个作为引用的参数都会得到它自己的生命周期参数。
  • 2.第二个规则是,如果只有一个输入生命周期参数,那么该生命周期将被分配给所有输出生命周期参数(该生命周期将分配给返回值)。
  • 3.第三个规则是,如果有多个输入生命周期参数,但其中一个是对self或不可变 self 的引l用时。因为在这种情况下它是一个方法,所以 self 的生命周期被分配给所有输出生命参数就是怎么标识这个生命周期

Example:

rust 复制代码
// 写法1
// 强制s1和s2的生命周期相同:'a就是个标识
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

// 写法2:性能好点,但是大多数时用不到
fn longest_str<'a, 'b, 'out>(s1: &'a str, s2: &'b str) -> &'out str
// 取出生命周期'a和'b的最大的
where
    'a: 'out,
    'b: 'out,
{
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

// 多个输入参数,需要指定生命周期
fn no_need(s: &'static str, s1: &str) -> &'static str {
    s
}
fn main() {
    println!("no need {}", no_need("hh", "ssss"));

    let s1 = "hello world";
    let s2 = "hello";
    println!("longest {}", longest(s1, s2));
    let result: &str;
    {
        let r2 = "world";
        result = longest_str(r2, s1);
    }

    println!("Longest string: {}", result);
}

编译及运行:

bash 复制代码
 cargo run
   Compiling ch21_func v0.1.0 (/home/wangji/installer/rust/project/ch21_func)
warning: unused variable: `s1`
  --> src/main.rs:26:29
   |
26 | fn no_need(s: &'static str, s1: &str) -> &'static str {
   |                             ^^ help: if this is intentional, prefix it with an underscore: `_s1`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: `ch21_func` (bin "ch21_func") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
     Running `target/debug/ch21_func`
no need hh
longest hello world
Longest string: hello world

三、Lifetime与struct

1.结构体中的引用

在结构体中的引用需要标注生命周期

结构体的方法(&self等)不需要标注生命周期

rust 复制代码
struct MyString<'a> {
    text: &'a str, // String,一般不要使用 &'a str
}

impl<'a> MyString<'a> {
    fn get_length(&self) -> usize {
        self.text.len()
    }
    fn modify_data(&mut self) {
        self.text = "world";
    }
}

struct StringHolder {
    data: String,
}

impl StringHolder {
    fn get_length(&self) -> usize {
        return self.data.len();
    }
    fn get_reference<'a>(&'a self) -> &'a String {
        &self.data
    }
    fn get_ref(&self) -> &String {
        &self.data
    }
}

fn main() {
    let str1 = String::from("value");
    let mut x = MyString {
        text: str1.as_str(), //String->&str
    };
    x.modify_data();
    println!("{}", x.text);
    let holder = StringHolder {
        data: String::from("Hello"),
    };
    println!("{}", holder.get_reference());
    println!("{}", holder.get_ref());
    println!("{}", holder.get_length());
}

编译及测试

bash 复制代码
 cargo run
   Compiling ch3_struct v0.1.0 (/home/wangji/installer/rust/project/ch22_struct)
warning: method `get_length` is never used
 --> src/main.rs:6:8
  |
5 | impl<'a> MyString<'a> {
  | --------------------- method in this implementation
6 |     fn get_length(&self) -> usize {
  |        ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `ch3_struct` (bin "ch3_struct") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
     Running `target/debug/ch3_struct`
world
Hello
Hello
5

参考

相关推荐
小熊吃保安16 小时前
Windows也能用 Apple Watch 解锁
windows·rust·开源
离歌漠1 天前
《我用 Tauri 做了一个本地开发工具箱》
rust·typescript·vue
小小龙学IT2 天前
Python Polars 高性能数据处理引擎深度解析
开发语言·python·rust·1024程序员节
zzu123zsw2 天前
为什么 Python 做 Agent 总在并发中崩溃?拆解 Blockcell 在 Rust 下的 Host-Cell 隔离与自愈状态机
开发语言·python·rust·大模型·ai agent
hsg772 天前
简述:基于Ubuntu24编译发布rust开发的html+web后端服务的方法
rust·1024程序员节
zzu123zsw2 天前
当 GPU 加速渲染遇上无头组件:拆解 Yororen UI 在 GPUI 生态下的状态解耦与 XML 模板实践
xml·ui·rust·gui·gpui
传奇开心果编程2 天前
【Rust入门练中学】第6课:结构体与方法
开发语言·rust·结构体·编程语言理论·结构体方法·编程通用语言
本地化文档2 天前
cutile-rs-book-l10n
rust·gpu·nvidia·cuda·sphinx·crowdin
qq_406981032 天前
大白话rust系列09 函数调用两次有什么错?
开发语言·python·rust
康小旺2 天前
TypeScript 编译器开始卷 Rust 了:Cloudflare 的 tsrs 到底想干嘛?
rust·typescript