美文网首页
rust 内存管理

rust 内存管理

作者: xiongzenghui | 来源:发表于2018-08-12 17:42 被阅读56次

    1、&变量 => 不可变取地址

    1. 传递内存地址

    fn run(x: &i32){
      println!("{}", x)
    }
    
    fn main()
    {
      let x = 5;
      run(&x);
    }
    
    ➜  main make
    rustc main.rs
    ./main
    5
    ➜  main
    

    2. 传递数组

    fn foo(s: &[i32]) {
      println!("{:?}", s)
    }
    
    fn main()
    {
      // Vec<T> implements Deref<Target=[T]>
      let owned = vec![1, 2, 3];
      foo(&owned);
    }
    
    ➜  main make
    rustc main.rs
    ./main
    [1, 2, 3]
    ➜  main
    

    3. 无法通过地址修改内存数据

    fn run(x: &i32){
      x += 1; // error:
      println!("{}", x)
    }
    
    fn main()
    {
      let mut x = 5;
      run(&x);
    }
    
    ➜  main make
    rustc main.rs
    error[E0368]: binary assignment operation `+=` cannot be applied to type `&i32`
     --> main.rs:2:3
      |
    2 |   x += 1; // error:
      |   -^^^^^
      |   |
      |   cannot use `+=` on type `&i32`
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    2、&mut 变量 => 可变取地址

    1. 可通过内存地址修改内存中数据

    fn main()
    {
      let mut x = 5; // mut可变绑定
      println!("x = {}", x);
    
      {
        let ptr = &mut x; // &mut 获取可变类型的内存地址
        *ptr += 1;
      }
    
      println!("x = {}", x);
    }
    
    ➜  main make
    rustc main.rs
    ./main
    x = 5
    x = 6
    ➜  main
    

    2. 函数形参为内存地址

    eg1

    fn run(ptr: &mut i32) 
    {
      *ptr += 1;
    }
    
    fn main()
    {
      let mut x = 5;
      println!("x = {}", x);
    
      // 先取&mut引用,再传递&mut引用给被调用函数
      {
        let ptr = &mut x;
        run(ptr);
      }
    
      println!("x = {}", x);
    }
    
    ➜  main make
    rustc main.rs
    ./main
    x = 5
    x = 6
    ➜  main
    

    eg2

    fn run(ptr: &mut i32) 
    {
      *ptr += 1;
    }
    
    fn main()
    {
      let mut x = 5;
      println!("x = {}", x);
    
      // 一步传地址
      run(&mut x);
    
      println!("x = {}", x);
    }
    
    ➜  main make
    rustc main.rs
    ./main
    x = 5
    x = 6
    ➜  main
    

    3、引用不被释放的内存

    1. 指向局部内存

    fn main()
    { 
      // 引用类型的变量
      let y: &i32;
    
      // 局部内存块
      { 
        let x = 5; // 局部内存
        y = &x; // 让外部的指针变量,指向局部内存块
      }
    
      // 通过指针访问已经被释放的内存块
      println!("{}", y);
    }
    
    ➜  main make
    rustc main.rs
    error[E0597]: `x` does not live long enough
      --> main.rs:9:10
       |
    9  |     y = &x; // 让外部的指针变量,指向局部内存块
       |          ^ borrowed value does not live long enough
    10 |   }
       |   - `x` dropped here while still borrowed
    ...
    14 | }
       | - borrowed value needs to live until here
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    对于 y = &x; 报错 => 使用了一个没有生命周期的内存

    borrowed value does not live long enough
    

    对于 x 报错 => x 已经被释放

    `x` dropped here while still borrowed
    

    换句话说,y 只在 X 存在的作用域内有效。一旦 x 消失了,它将会变成一个 x 的无效引用。因此,上面代码中的错误中说借用‘活的时间不够长’,因为它在有效的矢量的时间内是无效的。

    2. 引用变量在实例变量定义之前声明

    No

    fn main()
    { 
      // 先声明指针变量
      let y: &i32;
    
      // 再定义变量分配局部栈帧内存
      let x = 5;
    
      // 赋值指针变量指向栈帧上内存地址
      y = &x;
    }
    
    ➜  main make
    rustc main.rs
    error[E0597]: `x` does not live long enough
      --> main.rs:10:8
       |
    10 |   y = &x;
       |        ^ borrowed value does not live long enough
    11 | }
       | - `x` dropped here while still borrowed
       |
       = note: values in a scope are dropped in the opposite order they are created
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    同样是报错

    `x` does not live long enough
    
    `x` dropped here while still borrowed
    
    • 引用变量y先入栈,实例变量x后入栈,那么实例变量x处于栈顶
    • 栈帧弹出时,先弹出栈顶实例变量x的内存块,再弹出引用变量y的内存块
    • 就会造成引用变量y会引用一个已经被释放的内存块

    Yes

    fn main()
    { 
      // 再定义变量分配局部栈帧内存
      let x = 5;
    
      // 先声明指针变量
      let y: &i32;
    
      // 赋值指针变量指向栈帧上内存地址
      y = &x;
    }
    
    ➜  main make
    rustc main.rs
    ./main
    ➜  main
    

    4、所有权的转移与借用

    1. 内存所有权

    fn foo() {
      let v = vec![1, 2, 3];
    }
    
    • 进入foo()时将新创建新的Vec对象,并在堆区分配三个内存单元存储1、2、3
    • 局部变量v绑定拥有Vec对象所在内存块
    • 局部变量v超出foo()作用域时,会被自动清理掉
    • 那么Vec对象所在内存块也就失去了拥有者,所以也会被自动清理掉

    2. 内存块所有权的转移

    1. 【值赋值】方式会触发内存块所有权的转移

    fn main()
    {
      let v1 = vec![1, 2, 3]; // v1先持有vec对象内存块
      let v2 = v1; // v2也持有vec对象内存块,但是会自动解除v1对vec对象内存块的持有
      println!("v1[0] is: {}", v1[0]); // 此时v1不能再使用vec对象内存块
    }
    
    ➜  main make
    rustc main.rs
    error[E0382]: use of moved value: `v1`
     --> main.rs:5:28
      |
    4 |   let v2 = v1; // v2也持有vec对象内存块,但是会自动解除v1对vec对象内存块的持有
      |       -- value moved here
    5 |   println!("v1[0] is: {}", v1[0]); // 此时v1不能再使用vec对象内存块
      |                            ^^ value used here after move
      |
      = note: move occurs because `v1` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    核心错误提示:use of moved value: v1

    2. 【值传递】方式会触发内存块所有权的转移

    fn take(v: Vec<i32>) {
      // what happens here isn’t important.
    }
    
    fn main()
    {
      // mian()内变量v持有vec对象
      let v = vec![1, 2, 3];
    
      // 调用take(),使用【值传递】方式传递v持有的vec对象,
      // => 会触发对vec对象所有权的转移
      // => main()中的局部变量v此时会【解除】对vec对象的所有权
      take(v);
    
      // main()内的局部变量v无法再通过v读写vec对象的内存
      println!("v[0] is: {}", v[0]);
    }
    
    ➜  main make
    rustc main.rs
    error[E0382]: use of moved value: `v`
      --> main.rs:16:27
       |
    13 |   take(v);
       |        - value moved here
    ...
    16 |   println!("v[0] is: {}", v[0]);
       |                           ^ value used here after move
       |
       = note: move occurs because `v` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    核心错误提示:use of moved value: v

    3. Copy 拷贝取消默认的 Move控制权转移

    1. 基本数据类型的Copy

    fn main()
    {
      let a = 1;
      let b = a; // 并非使用的【内存所有权转移】,而是执行【内存块数据的拷贝】
      println!("a = {}", a); // 仍然可以使用变量a绑定的内存块
    }
    
    ➜  main make
    rustc main.rs
    ./main
    a = 1
    ➜  main
    

    正常执行。

    2. 函数返回传入的vec对象来恢复所有权

    fn foo(v: Vec<i32>) -> Vec<i32> {
      // do stuff with v
      v // 返回接收的vec对象,恢复被掉函数中变量的所有权
    }
    
    fn main()
    { 
      let v1 = vec![1, 2, 3];
    
      // => v1 失去所有权
      // => v2 在foo()执行完毕后返回时,获得所有权
      let v2 = foo(v1); 
    
      // println!("{:?}", v1); // error: use of moved value: `v1`
      println!("{:?}", v2); // ok
    }
    
    ➜  main make
    rustc main.rs
    ./main
    [1, 2, 3]
    ➜  main
    

    3. 当vec对象入参很多时,变得很复杂

    fn foo(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) {
      // do stuff with v1 and v2
    
      // 返回 v1, v2 => 为了让主调函数中恢复对传入的两个vec对象的所有权
      // 返回 42 => foo()运算结果值
      (v1, v2, 42)
    }
    
    fn main()
    {
      let v1 = vec![1, 2, 3];
      let v2 = vec![1, 2, 3];
    
      // => 调用foo()时,v1、v2【失去】所有权
      // => foo()返回时,v1、v2【恢复】所有权
      let (v1, v2, answer) = foo(v1, v2);
    
      println!("answer = {:?}", answer);
      println!("v1 = {:?}", v1); // ok
      println!("v2 = {:?}", v2); // ok
    }
    
    ➜  main make
    rustc main.rs
    ./main
    answer = 42
    v1 = [1, 2, 3]
    v2 = [1, 2, 3]
    ➜  main
    

    4. 接收与传递都使用 &T 【借用】使用权,避免因为move报错

    eg1

    fn foo(v: &Vec<i32>) {
      // do stuff with v
    }
    
    fn main()
    { 
      let v1 = vec![1, 2, 3];
    
      // => 不用再通过函数返回至接收vec对象,来恢复对vec对象内存的所有权
      // => 直接传递【&变量】给被调用函数
      // => 【&变量】只是让被调用函数,【暂时借用】使用变量的内存块,并不涉及所有权转移
      foo(&v1); 
    
      // foo()执行完毕后,仍然可以使用v1读写vec对象内存
      println!("{:?}", v1); // ok
    }
    
    ➜  main make
    rustc main.rs
    ./main
    [1, 2, 3]
    ➜  main
    

    eg2

    fn foo(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {
      42
    }
    
    fn main()
    {
      let v1 = vec![1, 2, 3];
      let v2 = vec![1, 2, 3];
    
      // 同上例
      let answer = foo(&v1, &v2);
    
      println!("answer = {:?}", answer);
      println!("v1 = {:?}", v1); // ok
      println!("v2 = {:?}", v2); // ok
    }
    
    ➜  main make
    rustc main.rs
    ./main
    answer = 42
    v1 = [1, 2, 3]
    v2 = [1, 2, 3]
    ➜  main
    

    5. &T 引用,无法再被调用函数中修改传入的内存

    fn run(x: &i32){
      x += 1; // error:
      println!("{}", x)
    }
    
    fn main()
    {
      let mut x = 5;
      run(&x);
    }
    
    ➜  main make
    rustc main.rs
    error[E0368]: binary assignment operation `+=` cannot be applied to type `&i32`
     --> main.rs:2:3
      |
    2 |   x += 1; // error:
      |   -^^^^^
      |   |
      |   cannot use `+=` on type `&i32`
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    6. &mut T 引用,可以在被调用函数中修改传入的内存

    fn run(x: &mut i32) {
      *x += 1;
    }
    
    fn main()
    {
      let mut x = 5;
      println!("x = {}", x);
    
      run(&mut x);
      println!("x = {}", x);
    }
    
    ➜  main make
    rustc main.rs
    ./main
    x = 5
    x = 6
    ➜  main
    

    5、var、&var、&mut var 作用域问题

    1. var、&var 能出现在一个作用域内

    fn main()
    {
      // mut可变绑定
      let mut x = 5; 
      println!("x = {}", x);
    
      // &mut 获取可变类型的内存地址
      let ptr = &x; 
    
      // error: cannot borrow `x` as immutable because it is also borrowed as mutable
      println!("x = {}", x); 
    }
    
    ➜  main make
    rustc main.rs
    ./main
    x = 5
    x = 5
    ➜  main
    

    2. var、&mut var 不能出现在一个作用域内

    fn main()
    {
      // mut可变绑定
      let mut x = 5; 
      println!("x = {}", x);
    
      // &mut 获取可变类型的内存地址
      let ptr = &mut x; 
    
      // error: cannot borrow `x` as immutable because it is also borrowed as mutable
      println!("x = {}", x); 
    }
    
    ➜  main make
    rustc main.rs
    warning: unused variable: `ptr`
     --> main.rs:8:7
      |
    8 |   let ptr = &mut x;
      |       ^^^
      |
      = note: #[warn(unused_variables)] on by default
      = note: to avoid this warning, consider using `_ptr` instead
    
    error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
      --> main.rs:11:22
       |
    8  |   let ptr = &mut x;
       |                  - mutable borrow occurs here
    ...
    11 |   println!("x = {}", x);
       |                      ^ immutable borrow occurs here
    12 | }
       | - mutable borrow ends here
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    
    • 提示不能访问x
    • 因为x已经被mutable

    3. &var、&mut var 也不能同时出现在一个作用域

    fn main()
    {
      // mut可变绑定
      let mut x = 5; 
      println!("x = {}", x);
    
      {
        let ptr1 = &x; //&T
        let ptr2 = &mut x;  //&mut T
      }
    }
    
    ➜  main make
    rustc main.rs
    error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
      --> main.rs:9:21
       |
    8  |     let ptr1 = &x;
       |                 - immutable borrow occurs here
    9  |     let ptr2 = &mut x;
       |                     ^ mutable borrow occurs here
    10 |   }
       |   - immutable borrow ends here
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    4. var、&var、&mut var 分作用域操作

    fn main()
    {
      // var => 主作用域
      let mut x = 5; 
      println!("x = {}", x);
        
      // &var => 子用域1
      {
        let ptr1 = &x; 
        println!("ptr1 = {}", ptr1);
      }
        
      // &mut var => 子用域2
      {
        let ptr2 = &mut x;
        *ptr2 += 1;
      }
        
      // var => 主作用域
      println!("x = {}", x);
    }
    
    ➜  main make
    rustc main.rs
    ./main
    x = 5
    ptr1 = 5
    x = 6
    ➜  main
    

    5. 在for迭代时,不能对容器同时进行修改

    fn main()
    {
      let mut v = vec![1, 2, 3];
    
      for i in &v { // 读迭代器
        println!("{}", i);
        v.push(34); // 写迭代器
      }
    }
    
    ➜  main make
    rustc main.rs
    error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
     --> main.rs:7:5
      |
    5 |   for i in &v { // 读迭代器
      |             - immutable borrow occurs here
    6 |     println!("{}", i);
    7 |     v.push(34); // 写迭代器
      |     ^ mutable borrow occurs here
    8 |   }
      |   - immutable borrow ends here
    
    error: aborting due to previous error
    
    make: *** [all] Error 101
    ➜  main
    

    不能修改 V,因为它在循环中被借用。

    相关文章

      网友评论

          本文标题:rust 内存管理

          本文链接:https://www.haomeiwen.com/subject/cdasbftx.html