👈 1 基础
存储结构——栈与堆:
- 堆:在内存中,存放大小不固定的数据。
- 栈:在高速缓存中,存放大小固定的数据(包括指向堆数据的指针)。
所有权原则:
- 每一个值被一个变量所拥有,变量称为值的所有者。
- 每一个值在同一时间只能被一个变量所拥有。
- 当所有者(变量)离开作用域范围时,属于它的值将被丢弃。
&str
:不可变的字符串字面量。
let s1: &str = "Rust";
String
:动态的、可变的字符串。
let s2: String = String::from("Rust");
let mut s = String::from("Rust");
s.push_str(", world!"); // 向 String 追加 &str
String
由 3 部分组成:指向存放字符串内容内存的指针,长度,容量。这 3 部分都存储在栈上,而其具体内容存储在堆上。
现在,执行 s1 = s2
,此时 Rust 有 3 种选择:
- 浅拷贝:将
s1
的栈上信息拷贝给s2
。
如果这样做,当 s1
和 s2
离开作用域时,它们尝试释放同一块内存,属于 二次释放(double free)错误,是一个内存安全性 bug。
- 深拷贝:同时拷贝
s1
的栈、堆信息给s2
。
在堆上数据比较大时,会对运行时性能造成很大影响。
- 移动(move):执行
s2 = s1
后,使s1
失效。这也是 Rust 实际采用的方式。
拷贝:
整数为基本数据类型,大小固定,存储在栈内,通过自动拷贝(浅拷贝)的方式赋值。
let x = 5; // 将值 5 绑定到变量 x
let y = x; // 将 x 的值拷贝, 赋给变量 y
// x 仍有效
String
为复杂类型,由栈内指针、字符串长度(已用大小)、字符串容量(分配大小)共同组成,大小不固定,存储在堆内,无法自动拷贝。
let x = String::from("Rust"); // 将 String::from 创建的值赋给变量 x
let y = x; // 所有权转移
// x 已失效
执行 let y = x
后,x
不再有效,对其值的所有权转移给 y
。
let x: &str = "Rust"; // 变量 x 引用字符串字面量
let y = x; // x 的引用值浅拷贝赋给 y
// x 仍有效
前一段中,x
持有了通过 String::from
调用创建的 String
值的所有权,而此处,x
只是引用了字符串字面量 "Rust"
,并不对该值持有所有权。
clone()
:创建数据的深拷贝,将大幅影响性能。
let x = String::from("Rust"); // 将 String::from 创建的值赋给变量 x
let y = x.clone(); // 将 x 的值深拷贝赋给 y
// x 仍有效
Copy
特征:拥有 Copy
特征的类型变量在被赋值给其他变量后不会失效。
具有 Copy
特征的类型:
- 基本类型的组合。
- 无需分配内存的或某种形式资源的类型。
例如,所有整数类型,所有浮点数类型,布尔类型,字符类型,元组,不可变引用 &T
。
作用域:
fn main() {
let s = String::from("Rust"); // s 的作用域开始
takes_ownership(s); // s 移动到方法内
// s 已失效
let a = 5; // a 的作用域开始
makes_copy(a); // a 移动到方法内
// a 仍有效
}
fn takes_ownership(x: String) {
println!("{x}");
} // x 移出作用域, 调用 drop(), 内存释放
fn makes_copy(x: i32) {
println!("{x}");
} // x 移出作用域, 无其他操作
要在调用 takes_ownership()
后继续使用 s
,只需改为传入 s.clone()
:
fn main() {
let s = String::from("Rust");
takes_ownership(s.clone()); // 改为传入 s.clone()
// s 仍有效
}
更多示例:
fn main() {
let s1 = gives_ownership(); // s1 的作用域开始
let s2 = String::from("Rust"); // s2 的作用域开始
let s3 = takes_and_gives_back(s2); // s2 移动到方法内, 方法的返回值转移给 s3
// s1 仍有效, s2 已失效, s3 仍有效
}
// 将返回值转移给调用者
fn gives_ownership() -> String {
let x = String::from("Hello");
x
}
// 接收一个值, 接着转移回调用者
fn takes_and_gives_back(x: String) -> String {
x
}
引用:
fn main() {
let x = 5; // x 的类型为 i32
let y = &x; // y 的类型为 &i32, 对 i32 的引用
assert_eq!(5, x); // success
assert_eq!(5, *y); // 解引用 y, 得到 i32 值 5 // success
}
传入引用不会转移所有权:
fn main() {
let s = String::from("Rust");
let n = get_len(&s);
// s 仍有效
}
fn get_len(s: &String) -> usize {
s.len()
}
引用指向的值默认不可变:
fn main() {
let s = String::from("Hello");
change(&s);
}
fn change(s: &String) {
s.push_str(", Rust!"); // error
}
改为可变引用则可修改:
fn main() {
let mut s = String::from("Hello"); // 改为可变
change(&mut s); // 改为传入可变引用
}
fn change(s: &mut String) { // 改为接收可变引用
s.push_str(", Rust!"); // ok
}
引用的作用域的结束位置:最后一次使用的行。
在同一作用域,一个变量在同一时间只能有一个可变引用:
// not ok
fn main() {
let mut s = String::from("Rust");
let r1 = &mut s;
let r2 = &mut s; // error: 无法同时对 s 进行 2 次可变借用
println!("{r1}, {r2}"); // r1 的最后一次使用
}
// ok
fn main() {
let mut s = String::from("Rust");
let r1 = &mut s;
print!("{r1}"); // r1 的最后一次使用, r1 的作用域在此结束
let r2 = &mut s; // 之后, 可以创建 r2
print!("{r2}");
}
// not ok
fn main() {
let mut s = String::from("Rust");
let r1 = &mut s;
print!("{}", r1);
let r2 = &mut s;
print!("{r2}");
print!("{r1}"); // 加上此行后出错, 因为它延长了 r1 的作用域, 导致 s2 和 s1 同时存在
}
// ok
fn main() {
let mut s = String::from("Rust");
let _r1 = &mut s; // r1 的作用域在此结束
let r2 = &mut s;
print!("{r2}");
}
可变引用与不可变引用不能同时存在:
fn main() {
let mut s = String::from("Rust");
let r1 = &s; // ok
let r2 = &s; // ok
let r3 = &mut s; // error
}
悬垂引用(dangling references):引用未释放,但其引用的变量已经释放。
fn main() {
let ref_to_nothing = dangle();
}
fn dangle() -> &String { // dangle() 返回一个字符串的引用
let s = String::from("hello"); // s 是一个新字符串
&s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃, 其内存被释放
// 危险!
悬垂指针和野指针的区别:
- 悬垂指针:曾经有效,现在失效。
- 野指针:从未有效过。
引用的总规则:
- 在任意给定时间,要么 只能有 一个 可变引用,要么 只能有多个 不可变 引用。
- 引用必须总是有效的。
练习:
fn main() {
// 使用尽可能多的方法过编
let x = String::from("Hello, Rust!");
let y = x;
println!("{x}, {y}");
}
答案:
fn main() {
let x = String::from("Hello, Rust!");
let y = x.clone(); // clone()
println!("{x}, {y}");
}
fn main() {
let x = "Hello, Rust!"; // 改为字面量
let y = x;
println!("{x}, {y}");
}
fn main() {
let x = &String::from("Hello, Rust!"); // 改为引用
let y = x;
println!("{x}, {y}");
}
fn main() {
let x = String::from("Hello, Rust!");
let y = x.as_str(); // 创建新变量
println!("{x}, {y}");
}
// 将 clone() 改为 copy
fn main() {
let x = (1, 2, (), "Rust".to_string());
let y = x.clone();
println!("{x:?}, {y:?}");
}
答案:
fn main() {
let x = (1, 2, (), "Rust"); // 改为字符串字面量
let y = x;
println!("{x:?}, {y:?}");
}
👉 3 复合类型