关于Rust中的所有权
Rust的核心特性就是所有权。
所有的程序在运行时都必须要管理它们使用的计算机内存的方式。不同于其他高级编程语言存在垃圾回收机制,或者程序员显式地分配内存。Rust采用另一种方式保证内存安全,使用所有权系统来管理内存,其中包含一组在编译时就会检查的规则。此外当程序运行时,所有权特性并不会减慢程序的运行速度。
栈stack内存和堆heap内存
1.栈和堆都是可供您的代码在运行时使用的内存部分,但它们的结构方式不同.堆stack按照获取值的顺序存储值(后进先出),添加数据称为入栈,删除数据称为出栈。
2.存储在堆栈上的所有数据必须具有已知的固定大小。编译时大小未知或大小可能更改的数据必须存储在堆heap上。
3.heap堆内存的组织性较差一些:
1)存储数据到heap时会请求一定数量的空间。
操作系统在heap里找到一块足够的内存标记为可用,并返回该空间的指针,即该空间的地址。这个过程叫做早heap上分配。
2)访问堆中的数据比访问栈中的数据慢,因为您必须遵循指针才能到达那里。
3)当您的代码调用函数时,传递给函数的值(可能包括指向堆上数据的指针)和函数的局部变量会被压入栈。当函数结束时,这些值会从栈中弹出。
所有权规则
1.Rust 中的每个值都有一个owner。
2.一次只能有一个所有者。
3.当所有者超出作用域时,该值将被删除。
可变范围
fn main(){ //s无效,因为此时s还未被声明
let s = "hello" //s进入范围,有效
let mut str = String::from("hello")
} //s超出范围,不在有效
变量及其有效范围:
- 当s进入范围时,它是有效的。
- 它在超出范围之前一直有效。
内存与分配
1.我们不能为每段在编译时大小未知且在运行程序时其大小可能会发生变化的文本在二进制文件中放入一块内存。
2.对于String类型,为了支持可变的,需要在堆上分配一定数量的内存来保存编译时未知大小的内容:
OS必须在运行时请求内存,String::from
结束后返回内存给OS
3.当变量走出其作用时,Rust会调用一个drop函数。
变量和数据交互的方式:又称为移动move
let x =5
let y=x
//整数是已知且固定大小的简单值,因此x和y被压入了stack栈内存中
let s1 = String::from("hello")
let s2 =s1
printlin!("{}",s2) // 报错,无法借用已经移动的值
String类型数据由三部分组成:
1.一个指向存放其内容的内存的指针
2.一个长度len,存放内容所需的字节数
3.一个容量capcity
这部分的内容被压入stack栈内存中,存放其内容的部分在堆heap内存中:
当let s2 =s1时,string的数据被复制了一份,在stack上,heap的内容并不复制。
此时当变量超出其作用域的时候,s1和s2都会尝试释放heap上对应的内存,这是就会造成验证的二次释放bug。因此需要所有权系统。Rust并不考虑复制heap堆内存的数据,因为其代价是昂贵的,需要更多的内存。
Rust进行以下操作:
1.当赋值后,让s1失效 ,这样s1在离开作用域时,就不会尝试释放任何东西。
2.对与heap上的数据如果希望复制一份,可使用clone()方法
3.copy trait,对于简单标量的组合类型都是可Copy的。
所有权和函数
把值传递给函数,要么移动到函数,要么复制
fn main() {
let s = String::from("hello");
takes_ownership(s); //s的值相当于被移动到该函数里,后续s就不在有效了
let x = 5;
makes_copy(x); //这个i32类型的数据并不发生移动,而是实现copy trait
printlin!({},x) //因此在作用域有效范围内,依旧是可用的
}
//s超出其作用,但由于s移动到takes_ownership函数内部,因此,其不会造成任何影响。x不在有效。
fn takes_ownership(some_string: String) { //s进入takes_ownership作用域
println!("{}", some_string);
}
//s离开takes_ownership,Rust会自动调用drop函数,返还内存
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
返回值和作用域
函数在返回值得时候也会发生所有权的转移
fn main() {
let s1 = gives_ownership(); //s1被创建,作用域内有效
let s2 = String::from("hello"); //s2从字面获得的String类型,
let s3 = takes_and_gives_back(s2); //通过函数,s2移动到了该函数内部
}
//s1超出作用域,失效。s2因为通过函数发生了“移动”,因此其失效,但不会产生任何特别的事。S3也超出作用域,失效。
fn gives_ownership() -> String {
let some_string = String::from("yours");
some_string
}
fn takes_and_gives_back(a_string: String) -> String { //s2为值得a_string进入函数的作用域,但是把值作为返回值返回,该函数在过程中取得s2的所有权,并返回该所有权
a_string
}
本文由 yuin 创作,
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。