Rust内存管理模型
"Stop the world'"是与垃圾回收(Garbage Collection)相关的术语,它指的是在进行垃圾回收时系统暂停程序的运行。这个术语主要用于描述一种全局性的暂停,即所有应用线程都被停止,以便垃圾回收器能够安全地进行工作。这种全局性的停止会导致一些潜在的问题,特别是对于需要低延迟和高性能的应用程序。
C/C++内存错误大全
-
内存泄漏
-
悬空指针
-
重复释放
-
数组越界
-
野指针
-
使用已经释放的内存
-
堆栈溢出
-
不匹配的new/delete malloc/free
Rust
-
所有权机制
-
借用(Borrowing)
-
不可变引用
-
可变引用
-
生命周期
-
引用计数
// 唯一输入和唯一输出不需要标注生命周期
fn first_world(s: &str) -> &str{
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate(){
if item == b' '{
return &s[0..i]
}
}
&s[..]
}
fn main(){
let s = "hello world";
println!("{}", first_world(s))
}
String 和 &str
String
是一个堆分配的可变字符串类型,具有所有权
pub struct String { vec:Vec<u8>, }
&str
是指字符串切片引用,是在栈上分配的,不具备所有权
不可变引用,指向存储在其他地方的UTF-8编码的字符串数据
由指针和长度构成
Struct中属性使用String
-
如果不使用显式声明生命周期无法使用&str
-
不只是麻烦,还有更多的隐患
函数参数推荐使用&str(如果不想交出所有权)
-
&str为参数,可以传递&str和&String
-
&String为参数,只能传递&String不能传递&str
struct Person<'a>{
name: &'a str,
color: String,
age: i32,
}
// &str &string
fn print(data: &str) {
println!("{data}");
}
// 只能传&string
fn print_string_borrow(data: &String){
println!("{data}");
}
fn main(){
let name = String::from("Value C++");
// String::from
// to_string()
// to_owned()
let course = "Rust".to_string();
let new_name = name.replace("C++", "CPP");
println!("{name} {course} {new_name}");
let rust = "\x52\x75\x73\x74"; // ascii
println!("{rust}");
let color = "yellow".to_string();
let name = "111";
let people = Person{
name: name,
color: color,
age: 18,
};
// func
let value = "value".to_string();
print(&value);
print("data");
print_string_borrow(&value);
}
枚举与匹配模式
枚举(enums)是一种用户自定义的数据类型,用于表示具有一组离散可能值的变量
-
每种可能值都称为"variant"(变体)
-
枚举名::变体名
枚举的好处
-
可以使你的代码更严谨、更易读
-
More robust programs
枚举内嵌类型
enum shape{
Circle(f64),
Rectangle(f64, f64),
}
常见的枚举类型
pub enum Option<T>{
None,
Some<T>,
}
pub enum Result<T, E>{
Ok<T>,
Err(E),
}
匹配模式
-
match关键字实现
-
必须覆盖所有的变体
-
可以使用_、..=、三元(if)等来进行匹配
match number {
0 => println!("Zero"),
1|2 => println!("One or Two"),
3..=9 => println!("From Three to Nine"),
n if n % 2 == 0 => println!("Even number"),
_ => println!("Other"),
}
enum Color {
Red,
Yellow,
Green,
}
fn print_color(my_color: Color){
match my_color {
Color::Red => println!("Red"),
Color::Yellow => println!("Yellow"),
Color::Green => println!("Green"),
}
}
enum BuildingLocation {
Number(i32),
Name(String),
Unknown,
}
impl BuildingLocation {
fn print_location(&self){
match self {
BuildingLocation::Number(c) => println!("Building Number {c}"),
BuildingLocation::Name(s) => println!("Building Name {s}"),
BuildingLocation::Unknown => println!("unknown"),
}
}
}
fn main(){
let a = Color::Red;
print_color(a);
let house = BuildingLocation::Name("111".to_string());
house.print_location();
}
结构体、方法、关联函数、关联变量
- 结构体
结构体是一种用户定义的数据类型,用于创建自定义的数据结构
struct Point { x: i32, y: i32, }
每条数据 (x 和 y) 称为属性(字段field)
通过点(.)来访问结构体中的属性
- 结构体中的方法
这里的方法是指,通过实例调用(&self、&mut self、self)
impl Point { fn distance(&self, other:&Point) → f64 { let dx (self.x - other.x) as f64; let dy (self.y - other.y) as f64; (dx * dx + dy *dy).sqrt() } }
- 结构体中的关联函数
关联函数是与类型相关联的函数,调用时为结构体名::函数名
impl Point{ fn new(x: u32, y: u32) → Self { Point {x, y} } }
- 结构体中的关联变量
这里的关联变量是指,和结构体类型相关的变量,也可以在特质或是枚举中
impl Point { const PI: f64 = 3.14 }
调用时使用 Point::PI
enum Flavor{
Spicy,
Sweet,
Fruity,
}
struct Drink{
flavor: Flavor,
price: f64,
}
impl Drink {
// 关联变量
const MAX_PRICE: f64 = 10.0;
// 方法
fn buy(&self){
if self.price > Self::MAX_PRICE {
println!("I am poor");
return ;
}
println!("buy it");
}
// 关联函数
fn new(price: f64) -> Self {
Drink {
flavor: Flavor::Fruity,
price,
}
}
}
fn print_drink(drink: Drink){
match drink.flavor {
Flavor::Fruity => println!("fruity"),
Flavor::Spicy => println!("spicy"),
Flavor::Sweet => println!("sweet"),
}
println!("{}", drink.price);
}
fn main(){
let sweet = Drink {
flavor: Flavor::Sweet,
price: 6.0
};
println!("{}", sweet.price);
let sweet = Drink::new(16.0);
sweet.buy();
}
Ownership与结构体
-
Each value in Rust has an owner
-
There can only be one owner at a time
-
Values are automatical ly dropped when the owner goes out of scope
每当将值从一个位置传递到另一个位置时,borrow checker都会重新评估所有权。
-
Immutable Borrow使用不可变的借用,值的所有权仍归发送方所有,接收方直接接收对该值的引用,而不是该值的副本。但是,他们不能使用该引用来修改它指向的值,编译器不允许这样做。释放资源的责任仍由发送方承担。仅当发件人本身超出范围时,才会删除该值
-
Mutable Borrow使用可变的借用所有权和删除值的责任也由发送者承担。但是接收方能够通过他们接收的引用来修改该值。
-
Move这是所有权从一个地点转移到另一个地点。borrow checker关于释放该值的决定将由该值的接收者(而不是发送者)通知。由于所有权已从发送方转移到接收方,因此发送方在将引用移动到另一个上下文后不能再使用该引用,发送方在移动后对vlaue的任何使用都会导致错误。
结构体中关联函数的参数
struct Counter {
number: i32,
}
impl Counter {
fn new(number: i32) -> Self {
Self { number }
}
// 不可变借用
fn get_number(&self) -> i32 {
self.number
}
// 不可变借用
fn add(&mut self, increment: i32) {
self.number += increment;
}
// move
fn give_up(self) {
println!("{}", self.number);
}
// move
fn combine(c1: Self, c2: Self) -> Self {
Self{
number: c1.number + c2.number,
}
}
}
fn main() {
let mut c1 = Counter::new(0);
println!("{}", c1.get_number());
c1.add(2);
println!("{}", c1.get_number());
}
堆与栈、Copy与Move
stack
-
堆栈将按照获取值的顺序存储值,并以相反的顺序删除值
-
操作高效,函数作用域就是在栈上
-
堆栈上存储的所有数据都必须具有已知的固定大小数据
heap
-
堆的规律性较差,当你把一些东西放到你请求的堆上时,你请求,请求空间,
并返回一个指针,这是该位置的地址 -
长度不确定
Box
Box是一个智能指针,它提供对堆分配内存的所有权。它允许你将数据存储在堆上而不是栈上,并且在复制或移动时保持对数据的唯一拥有权。使用Box可以避免一些内存管理问题,如悬垂指针和重复释放。
-
所有权转移
-
释放内存
-
解引用
-
构建递归数据结构
struct Point {
x: i32,
y: i32,
}
fn main(){
let box_point = Box::new(Point{x:10, y:20});
println!("x:{}, y:{}", box_point.x, box_point.y);
let mut box_point = Box::new(32);
*box_point += 10;
println!("{}", box_point);
}
Copy与Clone
Move:所有权转移
Clone:深拷贝
Copy:Copy是在CIone的基础建立的marker trait(Rust中最类似继承的关系)
-
trait(特质)是一种定义共享行为的机制。Clone也是特质
-
marker trait是一个没有任何方法的trait,它主要用于向编译器传递某些信息,以改变类型的默认行为