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()時將新創(chuàng)建新的Vec對象后专,并在堆區(qū)分配三個內存單元存儲1、2输莺、3
  • 局部變量v綁定擁有Vec對象所在內存塊
  • 局部變量v超出foo()作用域時戚哎,會被自動清理掉
  • 那么Vec對象所在內存塊也就失去了擁有者,所以也會被自動清理掉

2. 內存塊所有權的轉移

1. 【值賦值】方式會觸發(fā)內存塊所有權的轉移

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. 【值傳遞】方式會觸發(fā)內存塊所有權的轉移

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對象尸折,
  // => 會觸發(fā)對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; // 并非使用的【內存所有權轉移】,而是執(zhí)行【內存塊數據的拷貝】
  println!("a = {}", a); // 仍然可以使用變量a綁定的內存塊
}
?  main make
rustc main.rs
./main
a = 1
?  main

正常執(zhí)行殷蛇。

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()執(zhí)行完畢后返回時,獲得所有權
  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()執(zhí)行完畢后祖灰,仍然可以使用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,因為它在循環(huán)中被借用狞玛。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末软驰,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子心肪,更是在濱河造成了極大的恐慌锭亏,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硬鞍,死亡現場離奇詭異慧瘤,居然都是意外死亡,警方通過查閱死者的電腦和手機固该,發(fā)現死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門锅减,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人伐坏,你說我怎么就攤上這事怔匣。” “怎么了桦沉?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵每瞒,是天一觀的道長。 經常有香客問我纯露,道長剿骨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任埠褪,我火速辦了婚禮浓利,結果婚禮上,老公的妹妹穿的比我還像新娘钞速。我一直安慰自己贷掖,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布渴语。 她就那樣靜靜地躺著羽资,像睡著了一般。 火紅的嫁衣襯著肌膚如雪遵班。 梳的紋絲不亂的頭發(fā)上屠升,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天潮改,我揣著相機與錄音,去河邊找鬼腹暖。 笑死汇在,一個胖子當著我的面吹牛,可吹牛的內容都是我干的脏答。 我是一名探鬼主播糕殉,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼殖告!你這毒婦竟也來了阿蝶?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤黄绩,失蹤者是張志新(化名)和其女友劉穎羡洁,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體爽丹,經...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡筑煮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了粤蝎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片真仲。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖初澎,靈堂內的尸體忽然破棺而出秸应,到底是詐尸還是另有隱情,我是刑警寧澤碑宴,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布灸眼,位于F島的核電站,受9級特大地震影響墓懂,放射性物質發(fā)生泄漏。R本人自食惡果不足惜霉囚,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一捕仔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盈罐,春花似錦榜跌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至票顾,卻和暖如春础浮,著一層夾襖步出監(jiān)牢的瞬間帆调,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工豆同, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留番刊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓影锈,卻偏偏與公主長得像芹务,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸭廷,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內容

  • 《ijs》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 5,089評論 0 7
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,763評論 0 38
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5枣抱? 答:HTML5是最新的HTML標準。 注意:講述HT...
    kismetajun閱讀 27,447評論 1 45
  • 從三月份找實習到現在辆床,面了一些公司佳晶,掛了不少,但最終還是拿到小米佛吓、百度宵晚、阿里、京東维雇、新浪淤刃、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,209評論 11 349
  • 練字靜心吱型,而我練字也的確是為了靜心逸贾、修身。但是津滞,剛剛練完字的我卻格外的心煩铝侵。因為今天的這個字雖然簡單,只有六畫触徐,我...
    文小輝cool閱讀 216評論 0 0