所有权和借用
约 1070 字大约 4 分钟
2025-01-10
所有权
所有权的概念就是两个变量 a
b
将 a
赋值给了 b
a
就已经不再有效了,这就是把 a
的这块内存的所有权给了 b
等到 b
使用结束后,会将这块内存释放(现在的 b
,也就是原本的 a
)
这样也就不能使用 a
变量了,如果还需要使用,详见 克隆
基本数据类型
let x = 5;
let y = x;
这里 x
和 y
都是 5
因为 5
是基本数据类型,是直接通过自动拷贝实现赋值的
复杂类型
let s1 = String::from("hello");
let s2 = s1;
这里就不是通过自动拷贝实现的,因为 String
是一个复杂类型
克隆(深拷贝)
这样会创建一个新的内存空间来存储 s1
中的数据,然后存储的 s2
变量中
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
拷贝(浅拷贝)
也就是 基本数据类型 中的方法,因为他是基本数据类型,会自动拷贝
支持浅拷贝的类型:
类型 | 举例 | 描述 |
---|---|---|
整数 | u32 | |
浮点 | f64 | |
字符 | char | |
布尔 | bool | |
元组 | (i32, i32) | 里面的值需要也是支持浅拷贝的类型 |
不可变引用 | &T | 可变引用是不可以的,比如 &mut T |
函数传值和返回
这里的 s
因为是复杂类型,在传递给函数 takes_ownership
之后,原本的 s
的所有权被传递给了函数,如果需要使用这个 s
就会不存在了
fn main() {
let s = String::from("hello");
takes_ownership(s);
let x = 5;
makes_copy(x);
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
这样就导致了一个问题,如果把这个变量传递给了函数,但是这个变量我们还需要使用,那么我们就需要把这个变量再返回出来,这样很麻烦,这个时候我们就需要使用 引用/借用
这个概念了
引用与借用
引用就是一个指针,和 c语言
中的指针类似
fn main() {
let x = 5;
// 获取指针
let y = &x;
assert_eq!(5, x);
// 解引用
assert_eq!(5, *y);
}
那么我们现在来解决上面传递变量给函数的问题
不变引用
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
// 这里标记了传入的变量是一个指针,他会自动解引用,所以直接使用String上的函数即可
// 在结束这个函数之后因为是个内存指针,所以他不会被回收
s.len()
}
正如标题,这个引用是不可修改的,我们就需要用到 可变引用
了
可变引用
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
这样就可以实现可变引用了,不过同一个作用域里的一个数据只能存在一个可变引用
比如这样就会报错了,这样可以避免 数据竞争
的问题,这样很好,防止出现两个地方对一个变量进行修改,造成意外情况的出现
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
而且我们需要注意,可变引用
和 不可变引用
指定了同一个变量的情况也会报错
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 大问题
println!("{}, {}, and {}", r1, r2, r3);
因为我们使用了一个 不可变引用
的时候,肯定是不希望被别人修改了的,所以在获取了 不可变引用
了之后,是不能获取 可变引用
的
在 rust
中编译器会协助你避免 悬垂指针
的问题,例如
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
这样会导致报错,会提示缺少 生命周期
标注,因为你传递了指针就需要去界定,这个指针到哪里才会失效
你只需要将 指针
转换为 值
就可以正常返回了,例如:
fn no_dangle() -> String {
let s = String::from("hello");
s
}