「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。
所有权规则
- 每个值都有一个变量
- 每个值只能有一个所有者
- 当独所有者超出作用域,值被删除
变量作用域
scope 变量的有效范围
fn main() {
{
let s = "hello"; //s 可用
}// 作用域结束
println!("{}",s); // 作用域已经结束,s 已经不可用
}
String
为了演示所有权的规则,使用String类型,String类型很复杂.在这里只是用一下,在后面文章会详细说明
- 字符串字面:程序里手写的字符串.不可变
- 还有String 在head 上分配,能够存储在编译时未知数量的文本
创建String类型
- 使用from 函数,从字符串字面值创建String 类型
- 这个字符串可以被修改的
fn main() {
let mut s = String::from("hello"); //从字面量创建
s.push_str(", world!"); // 添加
println!("{}=>",s)
}
内存和分配
String 可以修改,字面量不行 ,因为他们处理的方式不同
- 字符串字面值,在编译时候就知道具体内容了,这些内容硬编码在可执行文件里面,
- 速度快,高效,因为其不可变性
- String 类型,为了支持可变性,需要在heap 上分配内存来保存编译时未知的文本
- 操作系统必须在运行时,请求内存 ,通过 String::form 来实现
- String 用完之后,需要把内存返回给操作系统()
在有GC语言中,GC会跟踪并清理不再使用的内存,没有GC的语言,很多需要手动释放,这就遇到了很多问题 1.忘了,浪费内存 2.提前释放了,变量不可用 3.释放此处多了,也是问题,必须保证一次分配一次释放 为了解决这么多问题,rust 采用了另一种方式
rust的处理方式
- 对于某个值,当拥有它的变量走出作用范围时,内存会立即自动的交还给操作系统
- drop函数,当变量走出作用域时候,自动调用(开发人员无需处理)
fn main() {
{
let s = "hello"; //s 可用
} // 当代码运行这一行时候,rust 会自动调用一个drop函数把空间释放
println!("{}",s); // 作用域已经结束,s 已经不可用
}
变量和数据交互的方式 :移动(Move)
String 由三部分组成:一个指向存放字符串内容的指针,一个长度,一个容量,这些放在stack上,存放的字符串放在heap上
- 长度:存放字符串内容所需的字节数
- 容量:String从操作系统获取的总字节数
下面上个demo
fn main() {
let s1 =String::from("hello");
let s2 = s1; // s1移动给s2
println!("{}",s1); //value borrowed here after move 移动之后又使用了这个值
}
rust为了保证内存安全,Rust 没有复制被分配的内存,Rust 让s1失效,
我们看到报错还有一行
move occurs because `s1` has type `String`, which does not implement the `Copy` trait
- Copy Trait 可用于想整数这样的完全放在stack上的类型
- 如果一个类型实现了Copy这个 trait ,那么旧的变量在赋值后 仍然可用
- 如果一个类型或者类型的一部分实现了Drop trait,那么Rust 不允许让它再去实现Copy trait
拥有 Copy trait的类型
- 任何简单标量的组合类型都可以是 Copy 的
- 任何需要分配内存或某种资源的都不是Copy的
- 一些拥有 Copy trait 的类型: 所有的整数类型, 例如 U32,bool,char,char
Tuple(元组) 如果其所有的字段都是Copy的 (i32, i32) 是 (i32, String)不是