天天看點

腐蝕rust哪個鍵可以拆除_用Rust寫腳本語言(十六):Rust代碼就是一層套一層套一層套一層的大洋蔥...

Cowsay:用Rust寫腳本語言(零):目錄​zhuanlan.zhihu.com

腐蝕rust哪個鍵可以拆除_用Rust寫腳本語言(十六):Rust代碼就是一層套一層套一層套一層的大洋蔥...

對不起各位,想法中預告的「大一統」至少還要再等一篇文章了。

我最大的錯誤就是,在預估項目進度的時候,總是習慣性的把Rust當成Python去考慮任務量。

實際上,Rust還是更像C++一點。那些讓項目無比紮實的boilerplate,無法省略也不應該省略。這是一件麻煩事,好的那種。當然了,Rust提供了比C++強大得多的宏系統來盡量精簡重複代碼,隻是我不太喜歡用而已。

更嚴重的問題是,Rust中的boilerplate還有可觀的一部分是由于生命周期的原因才引入的。這些代碼我在寫C++的時候也沒遇到過,是以真正遇到之前根本無法想到,居然會有這麼這麼這麼大的一坨……

今天不放項目位址了,現在的項目裡幾乎所有的子產品都被暫時注釋掉了,實在沒眼看。新添加的

SharedMemory

的用法在後面的文章裡再詳細講,這篇文章的重點是吐槽

impl<'a, O> RemoteObjectGuard<'a, O> {
    pub fn read(&self) -> ReadRemoteObject<O> {
        let read = ReadRemoteObject {
            guard: self
                .guard
                .objects
                .get(&self.object_id)
                .expect("segfault")
                .object
                .read()
                .unwrap(),
        };
        read
    }

    pub fn write(&self) -> WriteRemoteObject<O> {
        let write = WriteRemoteObject {
            guard: self
                .guard
                .objects
                .get(&self.object_id)
                .expect("segfault")
                .object
                .write()
                .unwrap(),
        };
        write
    }
}
           

恍惚間我真的以為自己在寫JavaScript。

首先我需要一個散清單,表本身可能被多個使用者插入/删除元素,是以需要

Arc<RwLock<...>>

。表本身放進鎖還不夠,鍵生成器也要放進去,于是我們有

pub struct SharedMemory<O> {
    internal: Arc<RwLock<SharedMemoryPriv<O>>>,
}
struct SharedMemoryPriv<O> {
    objects: HashMap<usize, CountedObject<O>>,
    object_id: Inc,
}
           

(哦對了,這個

XXPriv

還埋了一個坑,誰會知道

priv

居然也是Rust的保留字……)

那麼

CountedObject

又是什麼東西?在不改變散清單本身的情況下,寫一個對象不應該阻塞對另一個對象的讀寫,是以每一個對象都應該存入一個單獨的鎖中

struct CountedObject<O> {
    object: RwLock<O>,
    count: usize,
}
           

當有人想要通路對象時,

count

就會增加1;使用者不再需要它了,

count

就會減去1。通過類似于引用計數的方式決定一個對象是否需要存留在散清單中。說是「類似于」是因為對象之間不會互相引用,所有的引用計數來自于外部,是以沒有循環引用的問題。

也許你猜到了,「使用者不再需要它了」是通過RAII語義,實作

Drop

來表達的。多年以後,我成為了自己最讨厭的人……

也就是說,對一個對象的釋放持有操作有可能會影響散清單(沒人引用了的對象要從表中删除),是以交給使用者的對象代理不能僅僅包含對對象本身的控制權,還要能操作整個表

pub struct RemoteObject<O> {
    internal: Arc<RwLock<SharedMemoryPriv<O>>>,
    object_id: usize,
}
           

既然這裡出現了

RwLock<...>

, 有了之前文章中的慘痛教訓,我自然會意識到,

RwLockReadGuard<...>

是不可能省略的

pub struct RemoteObjectGuard<'a, O> {
    guard: RwLockReadGuard<'a, SharedMemoryPriv<O>>,
    object_id: usize,
}
           

然後呢?對于整個表的

RwLockReadGuard

都出現了,對于單個對象的還會遠嗎?

pub struct ReadRemoteObject<'a, O> {
    guard: RwLockReadGuard<'a, O>,
}
           

和表不同的是,單個對象還可以被可變借用

pub struct WriteRemoteObject<'a, O> {
    guard: RwLockWriteGuard<'a, O>,
}
           

七個,整整七個結構體,配上方法整整要寫170行,其中就包括最上面的那兩個方法,用于從整個表的

RwLockReadGuard

中提取對一個對象的guard。

這事告訴我們,Rust和Lock和RAII是真的真的不搭調。如果有機會,我要在Shattuck中試試看,有沒有不這麼反人類的寫法。

哎,真是一門「素雅」的語言啊……

Cowsay:用Rust寫腳本語言(零):目錄​zhuanlan.zhihu.com

腐蝕rust哪個鍵可以拆除_用Rust寫腳本語言(十六):Rust代碼就是一層套一層套一層套一層的大洋蔥...