理解 Rust 中的智能指針

開發人員在管理堆或棧上的數據時可以使用傳統的指針。但是,這些傳統的使用的指針方法有很多缺點,比如動態分配的對象沒有及時進行內存釋放時,會導致內存泄漏。好消息是,有更好的內存管理方法可以自動處理內存釋放,而且還不需要運行時成本,它們被稱爲智能指針。

Rust 是一種開源、底層、面向對象和靜態類型的編程語言,具有高效的內存管理,確保了高性能和安全性。它能夠用來構建高性能,高安全和健壯的應用程序,包括 web、移動、遊戲和網絡應用程序。

本文將讓你瞭解什麼是智能指針、它們的用例以及它們在 Rust 中的實現。所包含的代碼示例將向你介紹 Rust 各種類型的智能指針。

什麼是智能指針?

智能指針是抽象數據類型,在編程中與常規指針 (存儲值的內存地址的變量) 的作用類似,並附加了析構函數和重載操作符等附加功能。它們還包括自動內存管理,以解決內存泄漏等問題。

當開發人員將包含動態分配數據的內存與智能指針鏈接起來時,它們將被自動清理。

一些使用智能指針的用例:

智能指針在 Rust 中是如何工作的?

Rust 通過一個稱爲所有權的系統 (或一組規則) 實現內存管理,所有權包含在應用程序中,並在程序成功編譯前由編譯器檢查,而不會導致任何停機。

通過使用 struct,Rust 執行智能指針。在前面提到的智能指針的附加功能中,它們還具有擁有值本身的功能。

接下來,你將瞭解一些有助於在 Rust 中自定義智能指針操作的特徵。

Deref trait

Deref trait 用於有效的解引用,從而可以輕鬆訪問存儲在智能指針後面的數據。可以使用 Deref trait 將智能指針作爲引用。

Drop trait

Drop trait 類似於 Deref trait,但用於解構,Rust 通過清理程序不再使用的資源來自動實現解構。因此,Drop trait 用於釋放該值所佔用的內存空間。

要使用 Drop trait,你需要實現 drop() 方法,它帶有一個可變引用,用於對不再需要或超出範圍的值執行銷燬,定義爲:

fn drop(&mut self) {};

爲了更好地理解 Drop trait 是如何工作的,請看下面的例子:

struct Consensus  {
    small_towni32
}

impl Drop for Consensus {
    fn drop(&mut self) {
        println!("This instance of Consensus has being dropped: {}", self.small_town);
    }
}

fn main() {
    let _first_instance = Consensus{small_town10};
    let _second_instance = Consensus{small_town8};

    println!("Created instances of Consensus");
}

Drop trait 的 drop() 方法和可變引用,是通過在 struct 上使用 impl 關鍵字實現的。當 main() 函數中的實例超出作用域時調用 drop 方法,打印語句到控制檯。

Created instances of Consensus
This instance of Consensus has being dropped: 8
This instance of Consensus has being dropped: 10

智能指針用例

Rust 中存在很多種類型的智能指針。在這裏將通過代碼示例瞭解其中一些類型及其用例。它們包括:

Rc

Rc 表示引用計數智能指針類型。在 Rust 中,每個值每次都有一個所有者,而一個值有多個所有者是違反所有權規則的。然而,Reference counting 智能指針類型保存代碼中每個變量的引用計數。當引用的計數返回零時,它們不再被使用,智能指針將清理它們。

在下面的示例中,將創建三個列表。第一個列表將有兩個值,第二個和第三個列表將使用第一個列表作爲它們的第二個值。這意味着後兩個列表將與第一個列表共享所有權:

use std::rc::Rc;

enum List {
   Cons(i32, Rc<List>), Nil,
}

use List::{Cons, Nil};

fn main() {
   let _first_list = Rc::new(Cons(10, Rc::new(Cons(20, Rc::new(Nil)))));
   println!("The count after creating _first_list is {}", Rc::strong_count(&_first_list));

   let _second_list = Cons(8, Rc::clone(&_first_list));
   println!("The count after creating _second_list is {}", Rc::strong_count(&_first_list));

   { 
       let _third_list = Cons(9, Rc::clone(&_first_list));
       println!("The count after creating _third_list is {}", Rc::strong_count(&_first_list));
   }

   println!("The count after _third_list goes out of scope is {}", Rc::strong_count(&_first_list));
}

結果如下:

The count after creating _first_list is 1
The count after creating _second_list is 2
The count after creating _third_list is 3
The count after _third_list goes out of scope is 2

Box

Box 智能指針將值存儲在堆中, 表示數據類型。例子如下:

fn main() {
    let stack_data = 20;
    let hp_data = Box::new(stack_data); // points to the data in the heap
    println!("hp_data = {}", hp_data);  // output will be 20.
}

RefCell

RefCell 是一種智能指針類型,它在運行時而不是編譯時執行借用規則。使用 RefCell,可在運行時檢查可變和不可變借用。因此,如果你的代碼中有幾個不可變引用的數據,使用 RefCell,就可以更改數據。

#[derive(Debug)]
enum List {
  Cons(Rc<RefCell<i32>>, Rc<List>), Nil,
}

use List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
   let data = Rc::new(RefCell::new(10));

   let _first_list = Rc::new(Cons(Rc::clone(&data), Rc::new(Nil)));

   let _second_list = Cons(Rc::new(RefCell::new(9)), Rc::clone(&_first_list));

   let _third_list = Cons(Rc::new(RefCell::new(10)), Rc::clone(&_first_list));

   *data.borrow_mut() += 20;

   println!("first list after = {:?}", _first_list);
   println!("second list after = {:?}", _second_list);
   println!("third list after = {:?}", _third_list);
}

運行結果:

first list after = Cons(RefCell { value: 30 }, Nil)
second list after = Cons(RefCell { value: 9 }, Cons(RefCell { value: 30 }, Nil))
third list after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 30 }, Nil))

本文翻譯自:

https://blog.logrocket.com/smart-pointers-rust/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/TpjAp22O2I4HdlZaZzFADQ