在这个例子中,为什么我不能一次多次借用可变对象?

问题描述 投票:0回答:1

一个游戏的最小示例,玩家拥有一个位置并随着时间的流逝而走动。编译如下:

use std::thread::sleep;
use std::time::Duration;

struct Player {
    position: usize,
}

impl Player {
    fn new() -> Self {
        Self { position: 0 }
    }
}

impl Player {
    fn get_position(&self) -> usize {
        self.position
    }
}

impl Player {
    fn walk(&mut self) {
        self.position += 1;
    }
}

fn main() {
    let mut player = Player::new();
    loop {
        player.walk();
        sleep(Duration::from_secs(1));
    }
}

如果玩家借用该位置而不是拥有它,则不会编译:

use std::thread::sleep;
use std::time::Duration;

struct Player<'a> {
    position: &'a mut usize,
}

impl<'a> Player<'a> {
    fn new(position: &'a mut usize) -> Self {
        Self { position }
    }
}

impl<'a> Player<'a> {
    fn get_position(&'a self) -> usize {
        *self.position
    }
}

impl<'a> Player<'a> {
    fn walk(&'a mut self) {
        *self.position += 1;
    }
}

fn main() {
    let mut position = 0;
    let mut player = Player::new(&mut position);
    loop {
        player.walk();
        sleep(Duration::from_secs(1));
    }
}

错误内容为:

error[E0499]: cannot borrow `player` as mutable more than once at a time
  --> src/main.rs:30:9
   |
30 |         player.walk();
   |         ^^^^^^
   |         |
   |         `player` was mutably borrowed here in the previous iteration of the loop
   |         first borrow used here, in later iteration of loop

我不明白为什么当第一个程序可以编译时第二个程序不能编译。我知道编译器不接受多个可变借用,但第一个借用不是也是这种情况吗?该程序运行 walk 函数,该函数请求对 self 的可变引用,与第二个程序的方式相同。

在这样的情况下,程序需要借用结构体中的数据,然后对其进行修改,什么才是好的解决方案?

rust borrow-checker mutability
1个回答
0
投票

这是一个常见错误

impl<'a> Player<'a> {
    fn walk(&'a mut self) {

生命周期的名字是有意义的。由于您在结构和方法中使用相同的生命周期,因此您告诉编译器调用

walk
会为
Player<'a>
借用
'a
,这是
Player
借用的事物的生命周期。因此,您告诉编译器调用此方法借用该结构的时间超过了该结构可以存在的时间,本质上将您自己完全锁定在该结构之外。

解决方案很简单,只需删除方法中的一个即可:

impl<'a> Player<'a> {
    fn walk(&mut self) {

现在 Rust 可以创建第二个生命周期(你可以明确命名,只要它不是

'a
),并且它推断 that 生命周期只与函数体一样长,因为没有任何逃逸。

事实上,由于您不需要主动使用

'a
,您也可以将其保留为未命名,但您仍然必须将其传入,因为
Player
在整个生命周期中都是通用的,并且您不能忽略类型参数,但编译器非常满意

impl Player<'_> {
    fn walk(&mut self) {
© www.soinside.com 2019 - 2024. All rights reserved.