rust 内存管理

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,因为它在循环中被借用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容