自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何在十分鐘內(nèi)掌握Rust引用?

開發(fā) 前端
讓我們回顧一下引用規(guī)則,一個或多個不可變引用,或者僅僅是一個可變引用。在本例中,我們創(chuàng)建了兩個可變引用,借用檢查器將拒絕它們。但是這個規(guī)則實際上是有意義的,它可以保護免受內(nèi)存損壞錯誤的影響。

近年來,Rust已經(jīng)迅速成為最流行和增長最快的編程語言之一。谷歌和微軟等大型科技公司正在使用和投資它。它是一種允許帶有特殊約束的手動內(nèi)存管理的語言,這在很大程度上確保了內(nèi)存安全。

然而,Rust使用的約束(通常稱為借用檢查器)可能非常難以學習。嗯,如果你沒有正確學習的話。

這篇文章可以使你快速學習Rust中正確的引用概念。前提是你有一些Rust的基礎知識,比如結(jié)構體、函數(shù)和向量。

什么是引用?

引用是指在不顯式復制的情況下引用某些數(shù)據(jù)或變量的方法。Rust的引用與C和C++中的非混淆指針相同。在C和C++中,非混淆指針都是用restrict關鍵字定義的。在Rust中,引用采用的正是這種行為。但是,任何使引用相互命名別名的嘗試,無論是使用unsafe塊還是使用Rust的指針(這是另一個主題),都將導致未定義的行為。不要這樣做。

在Rust中,有四種方法可以將變量“傳遞”或轉(zhuǎn)移到函數(shù)或作用域之外。

1,移動變量:默認情況下,Rust會在賦值或從函數(shù)返回值時移動值。移動意味著一旦變量被移動,就不能在之前的位置使用它。

2,傳遞不可變引用:不可變引用是一種從另一個作用域引用變量的方法,只要該引用不會超出它所引用的變量的作用域。在Rust中,這被稱為生命周期。可以有一個或多個對變量的不可變引用。

3,傳遞可變引用:可變引用是引用來自另一個作用域的變量的一種方式,適用于類似的生命周期規(guī)則。但是,一個變量一次只有一個可變引用。這意味著在任何給定時間,任何變量都只能通過單個引用進行修改。

4,傳遞副本:在Rust中,不同的類型可以實現(xiàn)Copy或Clone特征,這樣它們就可以隱式或顯式地復制。Copy和Clone之間的主要區(qū)別在于前者是一個字節(jié)一個字節(jié)的memcpy風格復制,而Clone是顯式實現(xiàn)的一個成員一個成員的復制,可以使用自定義邏輯。

規(guī)則

引用的第一個也是最重要的規(guī)則是只有一個可變引用或多個不可變引用。但有一個問題是,這在實踐中看起來如何?讓我們來看幾個例子,從下面這個開始:

fn main() {
    let mut a = 6;
    let b = &a;
    let c = &mut a;
    println!("{}", *c);
}

上面的代碼實際上是有效的,你可能會認為同時存在不可變引用和可變引用。然而,需要注意的是,代碼只使用了c,沒有使用b下的不可變引用。由于這個原因,Rust的借用檢查器不會報錯。但是讓我們看看當我們開始使用b時會發(fā)生什么:

fn main() {
    let mut a = 6;
    let b = &a;
    let c = &mut a;
    println!("{}", *b);
}

這會導致編譯失?。?/p>

error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable
 --> src/main.rs:7:13
  |
6 |     let b = &a;
  |             -- immutable borrow occurs here
7 |     let c = &mut a;
  |             ^^^^^^ mutable borrow occurs here
8 |     println!("{}", *b);
  |                    -- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.

b被println!借走了,這會導致不可變和可變引用不能同時存在的規(guī)則被打破。

接下來,讓我們看一個更復雜的例子:

fn main() {
    let mut a = 6;
    let mut b = &a;
    let c = &mut b;
    println!("{}", *c);
}

乍一看,這看起來像是對同一個變量取了一個可變引用和一個不可變引用。然而,理解引用既是類型又是操作符是至關重要的。當使用引用操作符時,它接受與該操作符一起使用的變量的引用。

這意味著,c是對整數(shù)引用的可變引用。這個引用的Rust類型看起來像&mut&usize。在上面的代碼中,c可以被解引用并指向一個不同的&usize引用,這個引用會改變b,但不會改變a。如果我們試圖通過c來改變a,如下:

fn main() {
    let mut a = 6;
    let mut b = &a;
    let c = &mut b;
    **c += 1;
    println!("{}", *c);
}

會出現(xiàn)以下錯誤:

error[E0594]: cannot assign to `**c`, which is behind a `&` reference
 --> src/main.rs:8:5
  |
8 |     **c += 1;
  |     ^^^^^^^^ cannot assign

引用,類似于C/C++中的指針,可以形成任意長度的復合類型,這樣,&mut&mut&usize也可以作為Rust引用存在。與指針不同的是,引用的生命周期必須足夠長,否則,借用檢查器會讓你止步不前。

生命周期

在這里,我們可以探索各種引用的生命周期,并了解何時創(chuàng)建和銷毀引用(或者像Rust所說的“drop”)。下面的例子:

fn main() {
    let mut a = 6;
    let mut b = &a;
    {
        let c = 7;
        b = &c;
    }
    println!("{}", *b);
}

產(chǎn)生錯誤:

error[E0597]: `c` does not live long enough
  --> src/main.rs:9:13
   |
8  |         let c = 7;
   |             - binding `c` declared here
9  |         b = &c;
   |             ^^ borrowed value does not live long enough
10 |     }
   |     - `c` dropped here while still borrowed
11 |     println!("{}", *b);
   |                    -- borrow later used here

在內(nèi)部作用域中,b被改變?yōu)楸4鎸的引用。但是一旦內(nèi)部作用域結(jié)束,c就不存在了。因此,在這種情況下,引用比它引用的變量生命周期更長,所以產(chǎn)生了錯誤。

同樣的規(guī)則不適用于副本,因為副本是彼此獨立存在的。如果采用相同的代碼來刪除引用的使用:

fn main() {
    let mut a = 6;
    let mut b = a;
    {
        let c = 7;
        b = c;
    }
    println!("{}", b);
}

代碼編譯沒有錯誤。由于整數(shù)相對較小,因此通??梢詮椭扑鼈?。然而,更大的類型使用引用計數(shù)或按引用傳遞,以避免性能下降。

基于作用域的生命周期規(guī)則也適用于在較大的類實例中獲取引用。

struct Container(Vec<u64>);

impl Container {
    fn get(&self, index:usize) -> &u64 {
        &self.0[index]
    }
}

在上面的代碼中,get返回對vector中的引用,但是vector的生命周期必須比返回的引用長。如果我們應用同樣的邏輯,

fn main() {
    let m = Container(vec![1, 2, 3]);
    let mut the_ref = m.get(0);
    {
        let d = Container(vec![1, 2, 3]);
        the_ref = d.get(1);
    }
    println!("{}", the_ref);
}

此代碼也無法編譯,并出現(xiàn)類似的錯誤

error[E0597]: `d` does not live long enough
  --> src/main.rs:15:19
   |
14 |         let d = Container(vec![1, 2, 3]);
   |             - binding `d` declared here
15 |         the_ref = d.get(1);
   |                   ^ borrowed value does not live long enough
16 |     }
   |     - `d` dropped here while still borrowed
17 |     println!("{}", the_ref);
   |                    ------- borrow later used here

當某些東西在Rust中被刪除時,所有實現(xiàn)Drop特性的成員也將被刪除。

迭代和引用

當在迭代或循環(huán)中使用引用時,有幾種獨特的行為。如果迭代也是不可變的,則對集合類型的迭代,通常使循環(huán)充當該集合上的不可變借用的作用域。以下代碼為例:

fn main() {
    let mut a = vec![1, 2, 3, 4];
    for elem in a.iter() {
        if *elem % 2 == 0 {
            a.remove(*elem);
        }
    }
}

會導致編譯錯誤:

error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable
 --> src/main.rs:8:13
  |
6 |     for elem in a.iter() {
  |                 --------
  |                 |
  |                 immutable borrow occurs here
  |                 immutable borrow later used here
7 |         if *elem % 2 == 0 {
8 |             a.remove(*elem);
  |             ^^^^^^^^^^^^^^^ mutable borrow occurs here

Rust遵循這樣的規(guī)則:對某種類型的不可變迭代是一系列不可變借用,因此,不能在該迭代期間可變地借用相同的類型。

現(xiàn)在,你可能會認為這段特定代碼的解決方案是對其進行可變迭代。然而,這仍然是不正確的!如果將iter()改為iter_mut():

fn main() {
    let mut a = vec![1, 2, 3, 4];
    for elem in a.iter_mut() {
        if *elem % 2 == 0 {
            a.remove(*elem);
        }
    }
}

會出現(xiàn)以下錯誤:

error[E0499]: cannot borrow `a` as mutable more than once at a time
 --> src/main.rs:8:13
  |
6 |     for elem in a.iter_mut() {
  |                 ------------
  |                 |
  |                 first mutable borrow occurs here
  |                 first borrow later used here
7 |         if *elem % 2 == 0 {
8 |             a.remove(*elem);
  |             ^ second mutable borrow occurs here

讓我們回顧一下引用規(guī)則,一個或多個不可變引用,或者僅僅是一個可變引用。在本例中,我們創(chuàng)建了兩個可變引用,借用檢查器將拒絕它們。但是這個規(guī)則實際上是有意義的,它可以保護免受內(nèi)存損壞錯誤的影響。

根據(jù)集合的內(nèi)部實現(xiàn),修改集合類型會使現(xiàn)有迭代器失效。這可能是因為集合處理的內(nèi)存塊可能被分配或釋放,從而導致懸空指針,但是可變引用規(guī)則有效地防止了這種情況。

責任編輯:武曉燕 來源: coding到燈火闌珊
相關推薦

2022-08-26 09:01:07

CSSFlex 布局

2024-10-25 15:56:20

2020-12-17 06:48:21

SQLkafkaMySQL

2019-04-01 14:59:56

負載均衡服務器網(wǎng)絡

2023-09-26 22:12:13

數(shù)據(jù)倉庫Doris

2023-10-07 00:06:09

SQL數(shù)據(jù)庫

2009-10-09 14:45:29

VB程序

2021-07-29 08:57:23

ViteReact模塊

2021-09-07 09:40:20

Spark大數(shù)據(jù)引擎

2022-06-16 07:31:41

Web組件封裝HTML 標簽

2024-06-19 09:58:29

2023-04-12 11:18:51

甘特圖前端

2012-07-10 01:22:32

PythonPython教程

2015-09-06 09:22:24

框架搭建快速高效app

2024-05-13 09:28:43

Flink SQL大數(shù)據(jù)

2023-11-30 10:21:48

虛擬列表虛擬列表工具庫

2023-10-07 13:13:24

機器學習模型數(shù)據(jù)

2022-04-08 07:51:31

JavaJVM垃圾回收

2023-07-15 18:26:51

LinuxABI

2019-09-16 09:14:51

點贊
收藏

51CTO技術棧公眾號