在C/C++裏,我們經常做的一件事是,把一個複雜的對象按邏輯拆分成幾個部分(組件),然後實現各自的功能,對不對?接下來要面臨的一個問題是,有的時候某個組件還需要獲取來自於其他組件或者整體的信息。這個時候你會怎麼做呢?其中一個做法,就是在組件裏存儲一個整體對象的指針,用來從局部狀態出發,回頭獲取整體對象的狀態。

在Rust裡面臨的問題也是一樣的。有的時候,A是B的所有者,然而B的極少數功能可能就需要從A獲取數據,向A發送通知,之類的事情。那麼問題來了。在Rust裏我們平時不會像C/C++那樣在B裡面存儲一個A的借用(試試看,你會弄的一團糟)或者指針。存儲指針其實是不安全的,因為A是可能隨時被move走的,會導致你的指針失效。

那麼怎麼辦呢?

其實這個操作在Rust的概念模型下是直接可以實現的,甚至不需要unsafe,但是需要一點輔助代碼作為通用模板。訣竅就是:只要保持總體的借用一直都在就可以了。

我剛才試著寫了幾行代碼來完成這件事。

use std::ops::{Deref, DerefMut};

pub struct ChildRef<b, O: ?Sized + b, T: ?Sized> {
owner_ref: &b O,
accessor: fn(&O) -> &T,
}

impl<b, O: ?Sized, T: ?Sized> ChildRef<b, O, T> {
pub fn new(o: &b O, f: fn(&O) -> &T) -> Self {
ChildRef {
owner_ref: o,
accessor: f,
}
}

pub fn owner(&self) -> &O {
self.owner_ref
}
}

impl<b, O: ?Sized, T: ?Sized> Deref for ChildRef<b, O, T> {
type Target = T;

#[inline]
fn deref(&self) -> &T {
let acc = self.accessor.clone();
acc(self.owner_ref)
}
}

pub struct ChildMut<b, O: ?Sized + b, T: ?Sized> {
owner_ref: &b mut O,
accessor: fn(&O) -> &T,
accessor_mut: fn(&mut O) -> &mut T,
}

impl<b, O: ?Sized, T: ?Sized> ChildMut<b, O, T> {
pub fn new(o: &b mut O, f: fn(&O) -> &T, f_mut: fn(&mut O) -> &mut T) -> Self {
ChildMut {
owner_ref: o,
accessor: f,
accessor_mut: f_mut,
}
}

pub fn owner(&self) -> &O {
self.owner_ref
}

pub fn owner_mut(&mut self) -> &mut O {
self.owner_ref
}
}

impl<b, O: ?Sized, T: ?Sized> Deref for ChildMut<b, O, T> {
type Target = T;

#[inline]
fn deref(&self) -> &T {
let acc = self.accessor.clone();
acc(self.owner_ref)
}
}

impl<b, O: ?Sized, T: ?Sized> DerefMut for ChildMut<b, O, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
let acc = self.accessor_mut.clone();
acc(self.owner_ref)
}
}

這裡實現了一套指針,一個稱為ChildRef,一個稱為ChildMut,分別對應於不變、可變兩種情況。

接下來舉個使用的例子好了。我有兩個struct,Container裏擁有一個Item對象。

struct Item {
pub data: usize,
}

struct Container {
i: Item,
pub m: usize,
}

Container上面有讀寫i的方法(實現裏有輔助函數):

impl Container {
fn new() -> Self {
Container {
i: Item { data: 42usize },
m: 100usize,
}
}
fn get_item(&self) -> ChildRef<Container, Item> {
fn acc(s: &Container) -> &Item {
&s.i
}
ChildRef::new(self, acc)
}

fn get_item_mut(&mut self) -> ChildMut<Container, Item> {
fn acc(s: &Container) -> &Item {
&s.i
}
fn acc_mut(s: &mut Container) -> &mut Item {
&mut s.i
}
ChildMut::new(self, acc, acc_mut)
}
}

假設Item的result方法需要從Container上面獲取m的值,與自己的data相加,返回結果。這個時候,我們把這個方法實現在ChildRef上。注意owner()方法使我們可以獲取Container對象的引用。

impl<b> ChildRef<b, Container, Item> {
fn result(&self) -> usize {
self.owner().m + self.data
}
}

現在我們就可以像在C/C++裏一樣暢通無阻了~(中間可以加無數個.owner().get_item()哦)

fn main() {
let c = Container::new();
println!{"{}", c.get_item().owner().get_item().result() };
}

結果是142。

課後練習:不妨試試看,把為Container實現get_item、get_item_mut 的部分用Rust新出不久的 derive 宏來實現自動生成?

推薦閱讀:

相關文章