Cowsay:用Rust寫腳本語言(零):目錄zhuanlan.zhihu.com
對不起各位,想法中預告的「大一統」至少還要再等一篇文章了。
我最大的錯誤就是,在預估項目進度的時候,總是習慣性的把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