2024 Rust现代实用教程 Borrowing借用 Lifetime生命周期
文章目录
- 一、Borrowing && Borrow Checker && Lifetime
- 1.Borrowing
- 2. Borrow Checker
- 3.手动指定Lifetime
- 二、Lifetime与函数
- 1.任何引用都是有生命周期的
- 2.生命周期与函数
- 三、Lifetime与struct
- 1.结构体中的引用
- 参考
一、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版本的更新,会越来越强)
- 在函数/结构体签名中使用生命周期参数允许函数声明引用的有效范围。
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
}
编译及运行
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:
// 写法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);
}
编译及运行:
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等)不需要标注生命周期
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());
}
编译及测试
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
参考
- 2024 Rust现代实用教程