Rust 从子方法更改HashMap的键
我正在做一个2D沙盒游戏,在活塞中实现瓷砖。它的基本结构是一个Rust 从子方法更改HashMap的键,rust,Rust,我正在做一个2D沙盒游戏,在活塞中实现瓷砖。它的基本结构是一个Appstruct,它存储一个world:worldstruct,它具有tiles:HashMap属性Boxs,键是tile所在位置的X-Y位置 为了渲染它,我在屏幕上循环遍历每个tile空间的坐标,如果hashmap中有一个具有这些坐标的tile,我调用tile.render(&context,&mut-gl) 我现在需要实现一个Tile.update()方法,该方法将在每次更新时调用,但是该方法需要能够修改块的坐标,这意味着修改h
App
struct,它存储一个world:world
struct,它具有tiles:HashMap
属性Box
s,键是tile所在位置的X-Y位置
为了渲染它,我在屏幕上循环遍历每个tile空间的坐标,如果hashmap中有一个具有这些坐标的tile,我调用tile.render(&context,&mut-gl)代码>
我现在需要实现一个Tile.update()
方法,该方法将在每次更新时调用,但是该方法需要能够修改块的坐标,这意味着修改hashmap中Tile本身的键,但是我不能将hashmap传递给Tile.update()
函数,因为访问互动程序将借用hashmap
我如何以最简单/最优雅的方式实现这一点?有没有一种更好的方法可以让我储存瓷砖,让它发挥作用?无法从Tile.update()
方法外部更改坐标
这正是我想做的。我知道为什么它不起作用,我只是不知道如何绕过它:
use std::collections::HashMap;
use std::boxed::Box;
pub trait Tile {
fn new(x: i32, y: i32) -> Self where Self: Sized;
fn render(&self, camera_x: i32, camera_y: i32);
fn update(&mut self, app: &mut App);
}
pub struct DirtTile {
pub x: i32,
pub y: i32,
}
impl Tile for DirtTile {
fn new(x: i32, y: i32) -> DirtTile {
return DirtTile { x: x, y: y };
}
fn render(&self, camera_x: i32, camera_y: i32) {
// render
}
fn update(&mut self, app: &mut App) {
let world = app.world;
// update pos
let new_pos = [self.x + 1, self.y];
if world.tiles.get(&new_pos).is_none() {
world.tiles.remove(&[self.x, self.y]);
world.tiles.insert(new_pos, Box::new(*self));
self.x = new_pos[0];
self.y = new_pos[1];
}
}
}
pub struct World {
tiles: HashMap<[i32; 2], Box<Tile>>,
}
pub struct App {
world: World,
}
fn main() {
let mut app = App { world: World { tiles: HashMap::new() } };
app.world.tiles.insert([0, 0], Box::new(DirtTile::new(0, 0)));
for (xy, tile) in &app.world.tiles {
tile.update(&mut app);
}
}
使用std::collections::HashMap;
使用std::boxed::Box;
酒吧地砖{
fn新的(x:i32,y:i32)->Self,其中Self:Sized;
fn渲染(&self,摄影机x:i32,摄影机y:i32);
fn更新(&mut-self,应用程序:&mut-app);
}
pub-struct-DirtTile{
酒吧x:i32,
酒吧y:i32,
}
DirtTile的impl Tile{
fn新的(x:i32,y:i32)->DirtTile{
返回DirtTile{x:x,y:y};
}
fn渲染(&self,摄影机x:i32,摄影机y:i32){
//渲染
}
fn更新(&mut-self,应用程序:&mut-app){
让世界=app.world;
//更新pos
让new_pos=[self.x+1,self.y];
if world.tiles.get(&new_pos).is_none(){
world.tiles.remove(&[self.x,self.y]);
world.tiles.insert(new_pos,Box::new(*self));
self.x=新位置[0];
self.y=新位置[1];
}
}
}
酒吧结构世界{
tiles:HashMap,
}
发布结构应用程序{
世界:世界,,
}
fn main(){
让mut-app=app{world:world{tiles:HashMap::new()};
app.world.tiles.insert([0,0],Box::new(DirtTile::new(0,0));
对于&app.world.tiles中的(xy,tile){
tile.update(&mut应用程序);
}
}
您不能直接修改HashMap
的键。发件人:
对密钥进行修改,使其哈希(由哈希
特征确定)或其相等性(由Eq
特征确定)在映射中发生变化,这是一种逻辑错误
在那里,您必须移除并重新插入您已经尝试过的磁贴
。但是,在仍借用对象时,不能删除并重新插入。在更新
方法中,通过&self
借用平铺
对象。所以你必须在这个方法之外做
遗憾的是,在遍历HashMap
时,您也无法删除和重新插入,因为这会使迭代器无效。一种可能是在遍历地图时收集Vec
中所有修改的对象,然后将它们插入地图
但是,考虑使用不同的数据结构。在不知道您将拥有多少个瓷砖、它们改变位置的频率等情况下,很难对此进行推理。你可以像Minecraft那样将你的世界分成若干块,每一块都跨越一个大小为nxn的二次区域。然后,每一块都可以将它的分片保存在一个大小为
N^2
的Vec
中。但是,同样地,对于给定的信息,很难说什么样的数据结构最适合您。还有:这样的问题不太合适
一些补充意见:
- 您可以使用元组
(i32,i32)
而不是[i32;2]
,这样可以对其进行如下分解:用于&app.world.tiles中的((x,y),tile
)李>
- 您不需要将每个
磁贴
放入框
:哈希映射
已经拥有对象;将它们装箱只会引入一个额外的间接层。考虑使用不同的设计,允许您存储许多对象“内联”。框
引入了另一层间接寻址——这是现代CPU根本不喜欢的。关于这个话题的一本好书是
您不能直接修改
HashMap
的键。发件人:
对密钥进行修改,使其哈希(由哈希
特征确定)或其相等性(由Eq
特征确定)在映射中发生变化,这是一种逻辑错误
在那里,您必须移除并重新插入您已经尝试过的磁贴
。但是,在仍借用对象时,不能删除并重新插入。在更新
方法中,通过&self
借用平铺
对象。所以你必须在这个方法之外做
遗憾的是,在遍历HashMap
时,您也无法删除和重新插入,因为这会使迭代器无效。一种可能是在遍历地图时收集Vec
中所有修改的对象,然后将它们插入地图
但是,考虑使用不同的数据结构。在不知道您将拥有多少个瓷砖、它们改变位置的频率等情况下,很难对此进行推理。你可以像Minecraft那样将你的世界分成若干块,每一块都跨越一个大小为nxn的二次区域。然后,每一块都可以将它的分片保存在一个大小为
N^2
的Vec
中。但是,同样地,对于给定的信息,很难说什么样的数据结构最适合您。还有:这样,
let new_pos = [self.x + 1, self.y];
if world.tiles.get(&new_pos).is_none() {
world.tiles.remove(&[self.x, self.y]);
world.tiles.insert(new_pos, Box::new(*self));
self.x = new_pos[0];
self.y = new_pos[1];
}
use std::boxed::Box;
use std::collections::HashMap;
use std::fmt;
pub type TileMap = HashMap<(i32, i32), Box<Tile>>;
pub trait Tile: fmt::Debug {
fn new(x: i32, y: i32) -> Self where Self: Sized;
fn update_into(&self, app: &App, new_tiles: &mut TileMap);
}
#[derive(Copy, Clone, Debug)]
pub struct DirtTile {
pub x: i32,
pub y: i32
}
impl Tile for DirtTile {
fn new(x: i32, y: i32) -> DirtTile {
return DirtTile { x: x, y: y };
}
fn update_into(&self, app: &App, new_tiles: &mut TileMap) {
let world = &app.world;
let new_pos = (self.x + 1, self.y);
if world.tiles.get(&new_pos).is_none() {
new_tiles.insert(new_pos, Box::new(DirtTile::new(self.x + 1, self.y)));
}
else {
new_tiles.insert((self.x, self.y), Box::new(*self));
}
}
}
pub struct World {
tiles: TileMap,
}
pub struct App {
world: World,
}
fn main() {
let mut app = App { world: World { tiles: HashMap::new() } };
app.world.tiles.insert((0, 0), Box::new(DirtTile::new(0, 0)));
let mut new_tiles = HashMap::new();
for (_, tile) in &app.world.tiles {
tile.update_into(&app, &mut new_tiles);
}
app.world.tiles = new_tiles;
println!("{:?}", app.world.tiles);
}