在此示例中,为什么我不能一次多次以可变方式借用?

gui*_*tes 2 mutability rust borrow-checker

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

错误如下:

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
Run Code Online (Sandbox Code Playgroud)

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

在这样的情况下,程序需要借用结构体中的数据,然后修改它,什么被认为是一个好的解决方案?

Mas*_*inn 8

这是一个常见的错误

impl<'a> Player<'a> {
    fn walk(&'a mut self) {
Run Code Online (Sandbox Code Playgroud)

生命的名字是有意义的。由于您在结构和方法中使用相同的生命周期,因此您告诉编译器调用walk借用Player<'a>for 'a,这是借用事物的生命周期Player。因此,您告诉编译器调用此方法借用(可变地,因此不共享)结构的时间超过了结构可以存在的时间,本质上将您自己完全锁定在结构之外。

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

impl<'a> Player<'a> {
    fn walk(&mut self) {
Run Code Online (Sandbox Code Playgroud)

现在 Rust 可以创建第二个生命周期(您可以显式命名,只要它不是'a),并且它推断生命周期仅与函数体一样长,因为没有任何逃逸。

事实上,由于您不需要主动使用'a,您也可以不命名它,您仍然必须将其传入,因为它Player在生命周期内是通用的,并且您不能只省略类型参数,但编译器是完美的高兴

impl Player<'_> {
    fn walk(&mut self) {
Run Code Online (Sandbox Code Playgroud)