Rust 語言學習之旅(7)

banq發表於2022-09-29

Vec<T> 和 String
Vec<T> 是一個智慧指標,它只擁有一些位元組的記憶體區域。 Rust 編譯器不知道這些位元組中存在著什麼。 智慧指標解釋從它管理的記憶體區域獲取資料意味著什麼,跟蹤這些位元組中的資料結構開始和結束的位置,最後將指標解引用到資料結構中, 成為一個漂亮乾淨的可以閱讀的介面供我們使用(例如my_vec[3])。
類似地,String 跟蹤位元組的記憶體區域,並以程式設計方式將寫入其中的內容限制為始終有效的 utf-8,並幫助將該記憶體區域解引用為型別 &str。
這兩種資料結構都使用不安全的解引用指標來完成它們的工作。
記憶體細節:
  • Rust 有一個相當於 C 的 malloc方法, alloc 和 Layout 來獲取你自己管理的記憶體區域。


use std::alloc::{alloc, Layout};
use std::ops::Deref;

struct Pie {
    secret_recipe: usize,
}

impl Pie {
    fn new() -> Self {
        // let's ask for 4 bytes
        let layout = Layout::from_size_align(4, 1).unwrap();

        unsafe {
            // 分配記憶體位置並將其儲存為數字
            let ptr = alloc(layout) as *mut u8;
           // 使用指標數學並寫一些
            // u8 值到記憶體
            ptr.write(86);
            ptr.add(1).write(14);
            ptr.add(2).write(73);
            ptr.add(3).write(64);

            Pie { secret_recipe: ptr as usize }
        }
    }
}
impl Deref for Pie {
    type Target = f32;
    fn deref(&self) -> &f32 {
        // 將 secret_recipe 指標解釋為 f32 原始指標
        let pointer = self.secret_recipe as *const f32;
       // 將其解引用為返回值 &f32
        unsafe { &*pointer }
    }
}
fn main() {
    let p = Pie::new();
    // 透過取消引用我們的“make a pie”
    // Pie 結構智慧指標
    println!("{:?}", *p);
}


Box 是一個可以讓我們將資料從棧移動到堆的智慧指標。
解引用可以讓我們以人類更容易理解的方式使用堆分配的資料,就好像它是原始型別一樣。

struct Pie;

impl Pie {
    fn eat(&self) {
        println!("tastes better on the heap!")
    }
}

fn main() {
    let heap_pie = Box::new(Pie);
    heap_pie.eat();
}


引用計數Rc
Rc 是一個能將資料從棧移動到智慧指標。 它允許我們克隆其他Rc智慧指標,這些指標都具有不可改變地借用放在堆上的資料的能力。
只有當最後一個智慧指標被刪除時,堆上的資料才會被釋放。

use std::rc::Rc;

struct Pie;

impl Pie {
    fn eat(&self) {
        println!("tastes better on the heap!")
    }
}

fn main() {
    let heap_pie = Rc::new(Pie);
    let heap_pie2 = heap_pie.clone();
    let heap_pie3 = heap_pie2.clone();

    heap_pie3.eat();
    heap_pie2.eat();
    heap_pie.eat();

    // all reference count smart pointers are dropped now
    // the heap data Pie finally deallocates
}


共享訪問RefCel
RefCell 是一個容器資料結構,通常由智慧指標擁有,它接收資料並讓我們借用可變或不可變引用來訪問內部內容。 當您要求借用資料時,它透過在執行時強制執行 Rust 的記憶體安全規則來防止借用被濫用
只有一個可變引用或多個不可變引用,但不能同時有!
如果你違反了這些規則,RefCell 將會panic。

use std::cell::RefCell;

struct Pie {
    slices: u8
}

impl Pie {
    fn eat(&mut self) {
        println!("tastes better on the heap!");
        self.slices -= 1;
    }
}

fn main() {
    // RefCell 在執行時驗證記憶體安全性
     // 注意:pie_cell 不是 mut!
    let pie_cell = RefCell::new(Pie{slices:8});
    
    {
        // 但我們可以借用可變引用!
        let mut mut_ref_pie = pie_cell.borrow_mut();
    


執行緒間共享
Mutex 是一種容器資料結構,通常由智慧指標持有,它接收資料並讓我們借用對其中資料的可變和不可變引用。 這可以防止借用被濫用,因為作業系統一次只限制一個 CPU 執行緒訪問資料,阻塞其他執行緒,直到原執行緒完成其鎖定的借用。
多執行緒超出了 Rust 之旅的範圍,但 Mutex 是協調多個 CPU 執行緒訪問相同資料的基本部分。
有一個特殊的智慧指標 Arc,它與 Rc 相同,除了使用執行緒安全的引用計數遞增。 它通常用於對同一個 Mutex 進行多次引用。

組合智慧指標
智慧指標看起來可能會存在一些限制,但是我們可以做一些非常有用的結合。
Rc<Vec<Foo>> - 允許克隆多個可以借用堆上不可變資料結構的相同向量的智慧指標。
Rc<RefCell<Foo>> - 允許多個智慧指標可變/不可變地借用相同的結構Foo
Arc<Mutex<Foo>> - 允許多個智慧指標以 CPU 執行緒獨佔方式鎖定臨時可變/不可變借用的能力。
記憶體細節:

  • 您會注意到一個包含許多這些組合的主題。 使用不可變資料型別(可能由多個智慧指標擁有)來修改內部資料。 這在 Rust 中被稱為“內部可變性”模式。 這種模式讓我們可以在執行時以與 Rust 的編譯時檢查相同的安全級別來改變記憶體使用規則。


use std::cell::RefCell;
use std::rc::Rc;

struct Pie {
    slices: u8,
}

impl Pie {
    fn eat_slice(&mut self, name: &str) {
        println!("{} took a slice!", name);
        self.slices -= 1;
    }
}

struct SeaCreature {
    name: String,
    pie: Rc<RefCell<Pie>>,
}

impl SeaCreature {
    fn eat(&self) {
        // 使用指向 pie 的智慧指標進行可變借用
        let mut p = self.pie.borrow_mut();
        // take a bite!
        p.eat_slice(&self.name);
    }
}

fn main() {
    let pie = Rc::new(RefCell::new(Pie { slices: 8 }));
    // ferris 和 sarah 獲得了指向 pie 的智慧指標的克隆
    let ferris = SeaCreature {
        name: String::from("ferris"),
        pie: pie.clone(),
    };
    let sarah = SeaCreature {
        name: String::from("sarah"),
        pie: pie.clone(),
    };
    ferris.eat();
    sarah.eat();

    let p = pie.borrow();
    println!("{} slices left", p.slices);
}


智慧指標是 Rust程式設計中經常使用的,它可以讓我們不必重新建立非常常見的記憶體使用正規化。 有了它,您可以準備好應對最艱難的挑戰了!

Rust語言之GoF設計模式

相關文章