Rust与生命周期(一)

简介: Rust与生命周期(一)
生命周期可以说是Rust中最特别的性质,也是Rust中比较难(反人类)的部分。

生命周期(一)

关于生命周期,我们应该知道:

  • Rust的每个引用都有自己的生命周期
  • 生命周期:引用保持有效的作用域
  • 大多数情况:生命周期是隐式的、可被推断的
  • 当引用的生命周期可能以不同的方式互相关联时,我们需要手动标注生命周期

生命周期的目的

生命周期最主要的目的就是避免悬垂引用(dangling reference)

let r;
{
    let x = 5;
    r = &x; //`x` does not live long enough borrowed value does not live long enough
}
println!("r: {}", r);

上面会报错是因为在调用r的时候,x已经走出作用域,也就是被销毁了。而r被初始化为x的引用实际上就是一个悬垂引用

那么Rust是如何进行判断的呢?

借用检测器

Rust编译器的借用检查器:比较作用域来判断所有的借用是否合法。

image-20221218214749287

还是上面的例子,x的生命周期为b,r的生命周期为a。b的生命周期比a短。所以不合法。

如果要合法,那么x的生命周期至少不应该比r短。我们可以这样改动:

let x = 5;
let r = &x;

println!("r: {}", r);

函数中的泛型的生命周期

我们现在要写一个比较两个字符串长度的,并且返回长的那一个。逻辑很简单,我们可能会这样写:

fn main() {
    let s1 = "sss";
    let s2 = String::from("yesyesyes");
    println!("{}", longest(s1, s2.as_str()));
}

fn longest(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

但是却报错了,报错的原因是:此函数的返回类型包含借用的值,但签名没有说明它是从“S1”还是“S2”借用的。但是我们将其返回值为一个固定的,依然会报同样的错:

fn longest(s1: &str, s2: &str) -> &str {
    s2
}

所以这与函数体的逻辑无关,而是与函数签名有关。我们在这里就必须使用泛型的生命周期来编写代码:

fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

生命周期的标注语法

生命周期的标注不会改变引用的生命周期长度

当指定了泛型生命周期参数,函数可以接收带有任何生命周期的引用

生命周期的标注:描述了多个引用的生命周期间的关系,但不影响生命周期

生命周期标注的语法

生命周期参数名:

  • 以单引号'开头
  • 通常全小写且非常短
  • 通常情况下使用'a

生命周期标注的位置:

  • 在引用的&符号后
  • 使用空格将标注和引用类型分开

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

单个生命周期标注本身没有意义,标注之所以存在,是为了向rust描述多个泛型生命周期参数间的关系

fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

比如上面这个例子中就是说s1和s2这两个引用必须不能短于泛型生命周期'a

函数签名中的生命周期标注

泛型生命周期参数声明在:函数名和参数列表之间的<>里(泛型参数)

现在我们改动一下上面的例子:

fn main() {
    let s1 = String::from("yesyesyes");
    {
        let s2 = "sss";
        let result = longest(s1.as_str(), s2);
        println!("{}", result);
    }
}

fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

这里依然能通过,s1->2-8,s2生命周期为静态的生命周期(static),在整个程序运行期间都存活

&str是直接在可执行文件中加载的,所以指这块内存的引用,一定会一直指向一个合法内存,所以其引用的生命周期是'static,也就是全局静态

我们再改动一下代码:

fn main() {
    let s1 = String::from("yesyesyes");
    let result;
    {
        let s2 = String::from("sss");
        result = longest(s1.as_str(), s2.as_str());
    }
    println!("{}", result);
}

fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

我们将result声明在块外,println也在块外执行。到目前为止还没报错,但当我们将s2的类型转换为String类型的时候就报错了。

我们之前了解到:<'a>的生命周期是取的s1和s2中短的那一个。在这个例子中,'a的生命周期就是s2的生命周期,也就是5-7

使用cargo run来看看爆的是什么错误:

image-20221218222903172

我们看到,s2被借用了,但它的生命周期不够长。s2在离开作用域后也就是第7行后,在第8行依然被发生了借用,所以就报错了

我们只需要记住:生命周期'a的实际生命周期是:x和y两个生命周期中较小的那个

相关文章
|
4月前
|
Rust 安全 编译器
Rust中的生命周期与借用检查器:内存安全的守护神
本文深入探讨了Rust编程语言中生命周期与借用检查器的概念及其工作原理。Rust通过这些机制,在编译时确保了内存安全,避免了数据竞争和悬挂指针等常见问题。我们将详细解释生命周期如何管理数据的存活期,以及借用检查器如何确保数据的独占或共享访问,从而在不牺牲性能的前提下,为开发者提供了强大的内存安全保障。
|
2月前
|
Rust 安全 编译器
Rust中的生命周期管理:深入理解与实践
【7月更文挑战第10天】Rust中的生命周期是确保内存安全和避免数据竞争的关键机制。通过深入理解生命周期的概念、使用场景及省略规则,我们可以编写出更加安全、高效的Rust代码。虽然生命周期管理在初学时可能显得有些复杂,但一旦掌握,它将成为我们编写Rust代码时不可或缺的工具。希望本文能够帮助你更好地理解Rust中的生命周期管理,并在实际开发中灵活运用。
|
4月前
|
存储 Rust 安全
Rust 笔记:Rust 语言中的 所有权 与 生命周期
Rust 笔记:Rust 语言中的 所有权 与 生命周期
165 0
|
4月前
|
Rust 安全 编译器
Rust中避免常见错误:悬挂引用与生命周期不匹配
本文深入探讨了Rust编程语言中常见的两个内存管理错误:悬挂引用和生命周期不匹配,并提供了避免这些错误的实用方法。我们将详细解释这两种错误的来源,并通过示例展示如何在Rust中通过正确的生命周期标注和借用规则来避免它们,从而确保代码的内存安全性。
|
Rust 安全 Java
Rust学习笔记之泛型、trait 与生命周期
1. 泛型大补汤 推荐阅读指数 ⭐️⭐️⭐️⭐️ 2. 泛型数据类型 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 3. trait:定义共享的行为 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️ 4. 生命周期与引用有效性 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
Rust学习笔记之泛型、trait 与生命周期
|
Rust 编译器 程序员
Rust与生命周期(二)
Rust与生命周期(二)
|
21天前
|
Rust 安全 Go
揭秘Rust语言:为何它能让你在编程江湖中,既安全驰骋又高效超车,颠覆你的编程世界观!
【8月更文挑战第31天】Rust 是一门新兴的系统级编程语言,以其卓越的安全性、高性能和强大的并发能力著称。它通过独特的所有权和借用检查机制解决了内存安全问题,使开发者既能享受 C/C++ 的性能,又能避免常见的内存错误。Rust 支持零成本抽象,确保高级抽象不牺牲性能,同时提供模块化和并发编程支持,适用于系统应用、嵌入式设备及网络服务等多种场景。从简单的 “Hello World” 程序到复杂的系统开发,Rust 正逐渐成为现代软件开发的热门选择。
37 1
|
1月前
|
Rust 安全 编译器
初探 Rust 语言与环境搭建
Rust 是一门始于2006年的系统编程语言,由Mozilla研究员Graydon Hoare发起,旨在确保内存安全而不牺牲性能。通过所有权、借用和生命周期机制,Rust避免了空指针和数据竞争等问题,简化了并发编程。相较于C/C++,Rust在编译时预防内存错误,提供类似C++的语法和更高的安全性。Rust适用于系统编程、WebAssembly、嵌入式系统和工具开发等领域。其生态系统包括Cargo包管理器和活跃社区。学习资源如&quot;The Book&quot;和&quot;Rust by Example&quot;帮助新手入门。安装Rust可通过Rustup进行,支持跨平台操作。
102 2
初探 Rust 语言与环境搭建
|
21天前
|
Rust 安全 程序员
Rust 语言的防错机制太惊人了!安全编码从此不再是难题,快来一探究竟!
【8月更文挑战第31天】《安全编码原则:Rust 语言中的防错机制》探讨了代码安全的重要性,并详细介绍了Rust语言如何通过内存安全模型、所有权与借用规则等特性,在编译阶段检测并阻止潜在错误,如缓冲区溢出和悬空指针。文章还讨论了类型安全、边界检查等其他安全特性,并提出了遵循不可变引用、避免裸指针及充分测试等实用编码原则,以进一步提升代码质量和安全性。随着Rust在软件开发中的应用日益广泛,掌握其安全编码原则变得尤为重要。
31 0
|
21天前
|
Rust 安全 调度
从零构建梦想操作系统:用Rust语言亲手打造专属内核,你也可以成为系统开发者!
【8月更文挑战第31天】开发操作系统内核虽具挑战,却也充满乐趣。本文将指导你从零开始,使用Rust语言构建一个简单的操作系统内核。首先安装Rust环境和交叉编译工具链,然后创建新项目`my_kernel`。通过修改`Cargo.toml`和编写启动函数,结合串口输出和`multiboot2`库,最终使用QEMU运行内核。此教程旨在帮助你理解Rust在系统开发中的应用,激发深入探索的兴趣。
37 1