将自引用传递给包含对象的函数

Pass self reference to contained object's function

我正在尝试理解 Rust 的所有权模型。在结构上调用函数时,我试图传递对包含对象的引用。

这是我的结构:

pub struct Player {}

impl Player {
    pub fn receive(self, app: &App) {

    }
}

如您所见,receive 需要对 App 对象的引用。

pub struct App {
    pub player: Player,
}

impl App {
    pub fn sender(self) {
        // how to call player.test() and pass self as a reference?
        self.player.receive(&self);
    }
}

上面的代码给了我"use of partially moved value: self"。这是有道理的,因为 App 具有移动语义,所以该值在被调用时被移动到 sender 函数中。

如果我更改它以便 sender 引用 self,我得到 "cannot move out of borrowed content",这也有点道理,因为我们借用了 self 的引用=14=] 当我们进入 sender 函数时。

那我该怎么办?我明白为什么我不能在 Player 中存储对 App 的引用,因为那样会导致双向链接结构。但是我应该可以借用一个引用并对其进行操作,不是吗?

在官方教程中找不到答案

我通过在receive中传递self作为参考来解决它。但是,如果我希望 appreceive 中可变怎么办?我不能在 sender 中将 self 作为可变传递,因为我还借用了 player 作为可变。

because App has move semantics so the value was moved into the sender function when it was called.

它确实已移至 sender,但这不是此消息的内容。因为 Player::receive 按值获取 self,实际上您必须分解 app 并将 player 移出它才能调用 receive。在那个时间点,app现在已经半成型了;它没有 player 的有效值!如果 receive 尝试访问 app.player,它将使用无效内存。

"cannot move out of borrowed content" [...] because we've borrowed the reference to self when we went into the sender function.

对,这与上面有关。因为我们借用了一个 App,所以我们不能将 player 移出它,使 App 处于无效状态。

I should be able to borrow a reference and perform operations on it, no?

你可以,只要你参考的东西在那个时候已经完全形成。上面的说明中还有两个提示:

  1. If receive tried to access app.player

    如果您不访问 receive 中的 app.player,请重构您的代码以传递 App 的其他组件而不是整个容器。也许你有一些GameState是你真正想要传递的。

  2. leaving the App in a invalid state

    您可以使用 mem::replace 之类的东西将 不同的 Player 放入 app。然后它仍然完全(但不同)形成并且可以再次引用它。

当然,更实际的解决办法是改成accept references(&self)。

But what if I want app to be mutable in receive?

是啊!你会得到“不能一次多次借用 *self 作为可变”。然而,解决方案实际上基本相同!在调用方法之前,将 App 分解成更小的、不重叠的片段或从 self 中分离 player

一种遵循方式

disassociate player from self before calling the method.

就是把player放在一个Option中:

impl App {
    pub fn sender(&mut self) {
        let mut player = self.player.take();
        player.receive(&mut self);
        self.player = Some(player);
    }
}

最后一个资源是使用 RefCell