在 Rust 中处理大型可变结构的最佳方法是什么?

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

这是我的问题的简化视图:

struct Game {
    currentPlayer: u8,
    players: Vec<Player>,
    units: Vec<Unit>,
}

fn update(game: &mut Game) {
    for unit in game.units.iter_mut().filter(|u| u.owner == game.currentPlayer) {
        if unit.repairPrice < game.getPlayer(game.currentPlayer).money {
            unit.health = 100;
            game.getPlayer(game.currentPlayer).money -= unit.repairPrice;
        }
    }
}

impl Game {
    pub fn getPlayer(&mut self, player: u8) -> &mut Player {
        return self.players.iter_mut().find(|p| p.color == player).unwrap();
    }
}

在这里,编译器说我不能借用不可变的值 currentPlayer,因为游戏已经借用为可变的。

虽然我理解编译器在说什么以及为什么这么说,但我不知道我应该改变什么。单位和播放器都必须同时更新,所以看起来无论我怎么做总是会产生冲突。

我可以克隆整个结构,对其进行迭代和计算,然后在原始可变结构中找到相应的单位和玩家来更新它们,但这是非常低效的。

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

将您的代码扩展为问题的重现器,很明显,如果没有:,您就无法完成这项工作

    大幅重构,
  1. 大量浪费的克隆和/或
  2. unsafe
    的东西,或者
  3. getPlayer
    的代码内联到
    update
    (冗余,糟糕)
根本问题是,当一个函数说它可变地借用一个对象时,

它借用了该对象的整个。因此,对于 getPlayer,即使

you
知道它只触及 players 属性,Rust 的借用检查器(适用于函数
interfaces
,而不是 contents)必须假设它可以对 Game 执行任何操作收到的参考。
我能想到的最小解决方案是使 

getPlayer

成为一个自由函数,它只接受对

Vec
对象的 Player 的可变引用,因此
update
可以通过以下方式进行分析:明确表示
Vec
都不会被多次借用。
fn update(game: &mut Game) {
    for unit in game.units.iter_mut().filter(|u| u.owner == game.currentPlayer) {
        // Passed mutable reference to the players only, rather than being called on a whole Game
        if unit.repairPrice < getPlayer(&mut game.players, game.currentPlayer).money {
            unit.health = 100;
            // Same change as above
            getPlayer(&mut game.players, game.currentPlayer).money -= unit.repairPrice;
        }
    }
}

// Made a free function so it can accept the Vec alone, not the whole game
fn getPlayer(players: &mut Vec<Player>, player: u8) -> &mut Player {
    return players.iter_mut().find(|p| p.color == player).unwrap();
}

工作正常
(尽管它确实抱怨你的非惯用变量名称;
snake_case

FTW,这些mixedCase都不是废话)。

也就是说,我认为这里更好的方法可能是将 
Vec<Unit>
附加到

each

Player
,而不是将其放在 Game 上,而不是与玩家分开管理单位。这样做,
getPlayer
不会与
update
发生冲突,因为您只需可变地获取单个
Player
对象,并对附加到它的
unit
进行操作(而不接触
 的任何其他元素) Game
,并且操作更高效,因为您不需要继续查找玩家,不需要处理每个单位,只需处理该玩家的单位,等等)。如果您需要将所有
Unit
作为迭代器进行遍历,则简单的
flat_map
可以让您迭代玩家并将他们的单位组合成单个逻辑迭代器。
    

© www.soinside.com 2019 - 2024. All rights reserved.