Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/392.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Rust 从子方法更改HashMap的键_Rust - Fatal编程技术网

Rust 从子方法更改HashMap的键

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

我正在做一个2D沙盒游戏,在活塞中实现瓷砖。它的基本结构是一个
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);
      }