实现用于访问1D向量的2D向量语法?

omr*_*210 4 syntax vector operator-overloading rust

我正在制作玩具roguelike,并且有一个Level用于存储游戏地图的结构,其中最天真的实现是2D矢量.

我下面这个教程它采用VectorVectorS,但指出,对于性能提升它也可以使用一个单一的Vector大小MAP_HEIGHT * MAP_WIDTH,并在访问瓷砖(x, y)可以简单地访问map[y * MAP_WIDTH + x].

我正在尝试实现这种更快的方法,但使用getter和setter是笨重的,公共字段也不是那么好.我更喜欢它感觉像2D矢量.

为了做到这一点,我需要Index为我的班级实现特质,但我不确定如何得到我想要的结果.也许通过嵌套impls?我真的不知道.

这是我的代码尝试实现Index我的结构,这显然不适用于我的目的,因为它是一维的:

const MAP_WIDTH: i32 = 80;
const MAP_HEIGHT: i32 = 45;

pub struct Level {
    map: Vec<Tile>,
}

impl Level {
    pub fn new() -> Self {
        Level { map: vec![Tile::empty(); (MAP_HEIGHT * MAP_WIDTH) as usize] }
    }
}

impl std::ops::Index<i32> for Level {
    type Output = Tile;
    fn index(&self, x: i32) -> &Self::Output {
        self[MAP_WIDTH + x]; // We have x and y values; how do we make this work?
    }
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*all 5

使您的结构可以索引类型的对象(i32, i32).

type Pos = (i32, i32);

impl std::ops::Index<Pos> for Level {
    type Output = Tile;
    fn index(&self, (x, y): Pos) -> &Self::Output {
        &self.map[(y * MAP_WIDTH + x) as usize]
    }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以访问,例如:

let tile = level[(3, 4)];
Run Code Online (Sandbox Code Playgroud)

由于您正在使用i32,您需要确保这些值在范围内,并且可以强制执行usize,这是Vec索引的索引.可能你应该从一开始就坚持u32或者usize重视.否则,您需要跟踪最小值xy值,并减去它们,以保持位置在范围内.处理正坐标并假设地图的角是这样的假设,这肯定更简单(0, 0).


lje*_*drz 5

虽然不是很明显,但这是可能的.

首先,我建议具有MAP_WIDTHMAP_HEIGHTusize,因为它们是正整数:

const MAP_WIDTH: usize = 80;
const MAP_HEIGHT: usize = 45;
Run Code Online (Sandbox Code Playgroud)

然后你需要实现Index(并可能IndexMut)返回一个切片; 在这种情况下,我假设您希望第一个坐标为行:

impl std::ops::Index<usize> for Level {
    type Output = [Tile];

    fn index(&self, row: usize) -> &[Tile] {
        let start = MAP_WIDTH * row;
        &self.map[start .. start + MAP_WIDTH]
    }
}

impl std::ops::IndexMut<usize> for Level {
    fn index_mut(&mut self, row: usize) -> &mut [Tile] {
        let start = MAP_WIDTH * row;
        &mut self.map[start .. start + MAP_WIDTH]
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,当您索引a时Level,它首先返回具有适用行的切片; 然后,您可以使用列号索引该切片.

以下是替代示例实现Tile:

const MAP_WIDTH: usize = 80;
const MAP_HEIGHT: usize = 45;

#[derive(Clone, Debug)]
pub struct Tile {
    x: u32,
    y: u32
}

pub struct Level {
    map: Vec<Tile>,
}

impl Level {
    pub fn new() -> Self {
        Level { map: vec![Tile { x: 0, y: 0 }; (MAP_HEIGHT * MAP_WIDTH) as usize] }
    }
}

impl std::ops::Index<usize> for Level {
    type Output = [Tile];

    fn index(&self, row: usize) -> &[Tile] {
        let start = MAP_WIDTH * row;
        &self.map[start .. start + MAP_WIDTH]
    }
}

impl std::ops::IndexMut<usize> for Level {
    fn index_mut(&mut self, row: usize) -> &mut [Tile] {
        let start = MAP_WIDTH * row;
        &mut self.map[start .. start + MAP_WIDTH]
    }
}

fn main() {
    let mut lvl = Level::new(); 

    lvl[5][2] = Tile { x: 5, y: 2 };

    println!("{:?}", lvl[5][2]); // Tile { x: 5, y: 2 }
}
Run Code Online (Sandbox Code Playgroud)

  • 您应该知道,通过这样做,您将永远**要求您的内部数据结构是基于切片的东西.例如,你不能使用像"HashMap"这样的"稀疏"结构,因为它不能返回`&[Tile]`.返回`&[Tile]`的能力现在是结构的**公共API**的一部分.这种表示当然有可能发生变化,特别是因为OP已经改变了一次表示. (3认同)