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的,所以不会有任何问题。
怎么样,理解了么?