跳转至

Rust 之不可为-滥用getter

原文

https://zhuanlan.zhihu.com/p/31864925

上回说起在Rust里小伙伴们有时会犯的典型错误,这次我们接着说一个在某些语言里很常见的事物:getter。由于Rust当前的设计,使它有了特别的限制。和上次一样,先说结论。

结论

Rust里getter只可以用来访问结构的“自身”状态。当你的结构里存在多个field代表不同的“子系统”,并且它们之间存在交互的时候,把它们声明为pub field,而不要用getter返回field值的引用。

解释

这个规则乍看上去不是显而易见的。用一个例子带着大家经历一次吧。假设我们在写一个(过度简化的)游戏,它由场景管理SceneManager 和 资源管理 AssetManager组成。我们先来按照错误的做法开始。

#[derive(Default)]
struct Game {
    scenemgr: SceneManager,
    assetmgr: AssetManager,
}

impl Game {
    pub fn new() -> Self { Self::default() }

    pub fn scenemgr(&self) -> &SceneManager { &self.scenemgr }
    pub fn scenemgr_mut(&mut self) -> &mut SceneManager { &mut self.scenemgr }
    pub fn assetmgr(&self) -> &AssetManager { &self.assetmgr }
    pub fn assetmgr_mut(&mut self) -> &mut AssetManager { &mut self.assetmgr }

}

单这样看,还是挺和谐的,是吧?

好的,接下来请你从AssetManager中加载资源,然后用加载好的资源更新SceneManger的状态。

写起来你就会遇到一个问题了:你用getter拿到&mut SceneManager后,你没有办法再调用其他的getter,因为你处于可变借用期间……

要绕过这个问题,其中一个办法是写一个同时返回两样的getter……这样是可以工作的,但是不值得这样做。

另一个办法利用内部可变性来解决,把所有的状态修改封装成内部可变……这个办法也是可以工作的,但是也不值得这样做。

最好的办法就是我们开头提到的,不写这些getter,直接访问field。

这种办法是可以通过的。原因在于:Rust对于内部不同的访问路径是会分开记录borrow的,所以不会有任何问题。

怎么样,理解了么?