iT邦幫忙

2024 iThome 鐵人賽

DAY 9
1
Software Development

Rust 學得動嗎系列 第 9

[Day 9] Rust 的智慧指標與內部可變性:靈活管理記憶體和共享資料

  • 分享至 

  • xImage
  •  

今天,我們來學習 Rust 的智慧指標內部可變性概念。這些進階特性讓我們能更靈活地管理記憶體和共享資料,同時保持 Rust 的安全性保證。

1. 智慧指標

智慧指標是一種資料結構,不僅作為指標使用,還具有額外的元資料和功能。Rust 中最常用的智慧指標包括 Box<T>Rc<T>Arc<T>

Box

Box<T> 是最簡單的智慧指標,它允許我們將資料存儲在堆積上而不是堆疊上。

fn main() {
    // 在堆積上分配一個整數
    let x = Box::new(5);
    println!("x = {}", x);

    // 遞迴型別
    #[derive(Debug)]
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }

    use List::{Cons, Nil};

    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("list = {:?}", list);
}

https://ithelp.ithome.com.tw/upload/images/20240923/20140358cZEjgrbHA3.png

Rc

Rc<T>(參考計數)允許資料有多個所有者。它適用於需要在程式的多個部分共享資料的情況。

use std::rc::Rc;

fn main() {
    let x = Rc::new(5);
    let y = Rc::clone(&x);
    let z = Rc::clone(&x);

    println!("參考計數: {}", Rc::strong_count(&x));
    println!("x = {}, y = {}, z = {}", x, y, z);
}

https://ithelp.ithome.com.tw/upload/images/20240923/201403588t6wgTldIx.png

Arc

Arc<T>(原子參考計數)是 Rc<T> 的執行緒安全版本,適用於多執行緒環境。

use std::sync::Arc;
use std::thread;

fn main() {
    let x = Arc::new(5);
    let mut handles = vec![];

    for _ in 0..3 {
        let x = Arc::clone(&x);
        let handle = thread::spawn(move || {
            println!("x = {} in thread {:?}", x, thread::current().id());
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

https://ithelp.ithome.com.tw/upload/images/20240923/20140358wzb5aXtkIR.png

2. 內部可變性

內部可變性是 Rust 中一個重要的概念,它允許你在擁有不可變參考時修改資料。主要通過 RefCell<T>Mutex<T> 實現。

RefCell

RefCell<T> 提供了執行期借用檢查的內部可變性。

use std::cell::RefCell;

fn main() {
    let x = RefCell::new(5);

    {
        let mut borrowed = x.borrow_mut();
        *borrowed += 1;
    }

    println!("x = {:?}", x);

    let borrowed = x.borrow();
    println!("借用的值 = {}", *borrowed);
}

https://ithelp.ithome.com.tw/upload/images/20240923/20140358g0TLU7aY8k.png

Mutex

Mutex<T> 提供了執行緒安全的內部可變性。

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("計數結果:{}", *counter.lock().unwrap());
}

https://ithelp.ithome.com.tw/upload/images/20240923/20140358e1agFbY1hL.png

3. 結合智慧指標和內部可變性

我們可以結合使用這些概念來建立更複雜的資料結構。

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

#[derive(Debug)]
struct Node {
    value: i32,
    children: RefCell<Vec<Rc<Node>>>,
}

fn main() {
    let root = Rc::new(Node {
        value: 1,
        children: RefCell::new(vec![]),
    });

    let child1 = Rc::new(Node {
        value: 2,
        children: RefCell::new(vec![]),
    });

    let child2 = Rc::new(Node {
        value: 3,
        children: RefCell::new(vec![]),
    });

    // 動態新增子節點
    root.children.borrow_mut().push(Rc::clone(&child1));
    root.children.borrow_mut().push(Rc::clone(&child2));

    // 列印樹狀結構
    fn print_tree(node: &Node, indent: String) {
        println!("{}Node({})", indent, node.value);
        for child in node.children.borrow().iter() {
            print_tree(child, format!("{}  ", indent));
        }
    }

    print_tree(&root, String::new());
}

https://ithelp.ithome.com.tw/upload/images/20240923/20140358aRFKqy0Hpl.png

實際應用範例:簡單的緩存系統

讓我們使用智慧指標和內部可變性來實作一個簡單的緩存系統:

use std::collections::HashMap;
use std::hash::Hash;
use std::rc::Rc;
use std::cell::RefCell;

struct Cache<K, V> {
    map: RefCell<HashMap<K, Rc<V>>>,
}

impl<K: Hash + Eq, V> Cache<K, V> {
    fn new() -> Self {
        Cache {
            map: RefCell::new(HashMap::new()),
        }
    }

    fn get(&self, key: &K) -> Option<Rc<V>> {
        self.map.borrow().get(key).cloned()
    }

    fn insert(&self, key: K, value: V) {
        self.map.borrow_mut().insert(key, Rc::new(value));
    }
}

fn main() {
    let cache = Cache::new();

    cache.insert("key1", "value1");
    cache.insert("key2", "value2");

    match cache.get(&"key1") {
        Some(value) => println!("找到 key1:{}", value),
        None => println!("未找到 key1"),
    }

    match cache.get(&"key3") {
        Some(value) => println!("找到 key3:{}", value),
        None => println!("未找到 key3"),
    }

    // 展示共享所有權
    let value1 = cache.get(&"key1").unwrap();
    let value1_clone = Rc::clone(&value1);
    println!("原始值:{}, 複製值:{}", value1, value1_clone);
}

https://ithelp.ithome.com.tw/upload/images/20240923/20140358fgJ3mjQ8PM.png

明天,我們將探討 Rust 的進階特性,包括不安全 Rust、進階特徵和型別系統的更多細節。


上一篇
[Day 8] Rust 的並行程式設計:安全高效的多執行緒應用
下一篇
[Day 10] Rust 的進階特性:不安全程式碼、進階特徵與型別系統
系列文
Rust 學得動嗎30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言