前端搞Rust 第五集所有权的规则与内存分配

202 阅读3分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

所有权规则

  • 每个值都有一个变量
  • 每个值只能有一个所有者
  • 当独所有者超出作用域,值被删除

变量作用域

scope 变量的有效范围

fn main() {
    {
        let s = "hello"; //s 可用
        }// 作用域结束
    println!("{}",s); // 作用域已经结束,s 已经不可用
}

image.png

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从操作系统获取的总字节数

image.png 下面上个demo

fn main() {
    let s1 =String::from("hello");
    let s2 = s1; // s1移动给s2
    println!("{}",s1); //value borrowed here after move 移动之后又使用了这个值
}

image.png 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)不是