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

参考

相关推荐
SomeB1oody15 小时前
【Rust自学】4.1. 所有权:栈内存 vs. 堆内存
开发语言·后端·rust
SomeB1oody1 天前
【Rust自学】4.2. 所有权规则、内存与分配
开发语言·后端·rust
SomeB1oody1 天前
【Rust自学】4.5. 切片(Slice)
开发语言·后端·rust
编码浪子2 天前
构建一个rust生产应用读书笔记6-拒绝无效订阅者02
开发语言·后端·rust
baiyu332 天前
1小时放弃Rust(1): Hello-World
rust
baiyu332 天前
1小时放弃Rust(2): 两数之和
rust
Source.Liu2 天前
数据特性库 前言
rust·cad·num-traits
编码浪子2 天前
构建一个rust生产应用读书笔记7-确认邮件1
数据库·rust·php
SomeB1oody2 天前
【Rust自学】3.6. 控制流:循环
开发语言·后端·rust
Andrew_Ryan2 天前
深入了解 Rust 核心开发团队:这些人如何塑造了世界上最安全的编程语言
rust