学习自 rust圣经
强烈建议:https://gcc.godbolt.org/ 看反汇编
注意,下文的“新建栈上对象”,其实不是“申请”,而是机器代码控制,在函数体对应的机器代码之前,栈顶就保留了足够长的栈,然后栈底+offset是栈上对象的起始地址
rust所有权规则
- 每一个值(内存对象)都被一个变量所拥有,该变量被称为值的所有者
- 一个值(内存对象)同时(同一个作用域内)只能被一个变量所拥有,或者说一个值只能拥有一个所有者
- 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)
- 具体来说,栈上内存是编译器控制释放的(机器代码栈顶栈底),堆上内存等其他资源是类型的drop方法控制实现的,离开作用域时编译器产生调用drop的汇编代码
let底层
3个let典型例子
- 全在栈上的对象
fn main() {
let x = 5; // 栈上新建内存对象,其中内容放5(mov a 5这样子的)
let y = x; // 栈上新建内存对象,其中内容是复制x绑定的栈上内存对象的内容
}
- 栈上内存+堆上内存
fn main() {
// String类型如下图所示
let s1 = String::from("hello"); // 栈上新建内存对象,内容是指向堆的指针和长度;堆上新建内存对象,内容是hello的utf-8编码的字节数组
let s2 = s1; // 栈上新建内存对象,内容是s1栈上内存对象的拷贝
// (这样的话自然指针是指向原先堆上的那个数组的)
// s1之后不能被借用了
println!("{}, world!", s1); // 会报错
}

- 引用:全在栈上的对象
fn main() {
// 引用就是一个栈上对象,是一个指针,如下图所示
let x: &str = "hello, world";
let y = x;
println!("{},{}",x,y);
}
图示最左边的方块是&String类型

所以总结let:新建栈上对象(拷贝右值的栈上对象,或字面值(字面值会编码到机器指令中)),
如果右值对象有堆上对象,且没有实现Copy trait,则新对象的堆上对象就是右值对象的堆上对象(毕竟栈上对象是直接拷贝右值的,指针值还是指向原先的堆上对象),而右值对象会被编译器强制失效,不能再使用了
函数参数和返回值
传递参数,跟let语句一样
fn main() {
let s = String::from("hello"); // 新建堆栈对象
takes_ownership(s); // 看函数的注释
// s失效
let x = 5; // 新建栈对象
makes_copy(x); // 看函数的注释
// x仍然有效
}
fn takes_ownership(some_string: String) { // 新建栈对象,是拷贝传递来的参数的栈对象;堆对象是参数的堆对象,传递来的参数被失效
println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。堆内存被释放
fn makes_copy(some_integer: i32) { // 新建栈对象。由于i32是copy的,因此传递进来的参数不会被失效
println!("{}", some_integer);
}
返回值:把返回值的栈上部分放寄存器
fn main() {
let s1 = gives_ownership(); // gives_ownership 将返回值
// 移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到
// takes_and_gives_back 中,
// 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
// 所以什么也不会发生。s1 移出作用域并被丢弃
fn gives_ownership() -> String {
let some_string = String::from("hello");
some_string // 返回 some_string 并移出给调用的函数
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}
引用的作用域
引用的作用域 s 从创建开始,一直持续到它最后一次使用的地方,这个跟变量的作用域有所不同,变量的作用域从创建持续到某一个花括号 }
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
// 新编译器中,r1,r2作用域在这里结束
let r3 = &mut s; // 新编译器中,这样的代码不违反 同一时间不能同时有可变和不变引用
println!("{}", r3);
} // 老编译器中,r1、r2、r3作用域在这里结束
// 新编译器中,r3作用域在这里结束