简体   繁体   中英

Instantiating a 2d Vec in a Struct?

I'm having trouble instantiating a vec when using a constructor to return a new struct object. The syntax I've tried (using collect() improperly, probably) spat out a ton of compiler errors.

fn main() {
    let level = Level::new();
}

struct Level {
    tiles: Vec<Vec<Tile>>
}

struct Tile {
    idx: i32
}

impl Level {
    fn new() -> Level {
        Level {
            tiles: {
            let mut t = Vec::new();
            let mut t2 = Vec::new();
            for x in range(0, 80) {
                for y in range(0, 24) {
                    t2.push(Tile::new(x, y));
                }
                t.push(t2);
            }
            t
        }
    }
}

impl Tile {
    fn new(x: i32, y: i32) -> Tile {
        Tile { pos: Point { x: x, y: y } }
    }
}

struct Point {
    x: i32,
    y: i32
}

I get these errors:

src/game/dungeon/level/mod.rs:47:25: 47:27 error: use of moved value: `t2`
src/game/dungeon/level/mod.rs:47                 t2.push(Tile::new(x, y));
                                                     ^~
src/game/dungeon/level/mod.rs:49:28: 49:30 note: `t2` moved here because it has type `collections::vec::Vec<game::dungeon::level::Tile>`, which is non-copyable
src/game/dungeon/level/mod.rs:49                     t.push(t2);
                                                        ^~
src/game/dungeon/level/mod.rs:49:28: 49:30 error: use of moved value: `t2`
src/game/dungeon/level/mod.rs:49                     t.push(t2);
                                                        ^~
src/game/dungeon/level/mod.rs:49:28: 49:30 note: `t2` moved here because it has type `collections::vec::Vec<game::dungeon::level::Tile>`, which is non-copyable
src/game/dungeon/level/mod.rs:49                     t.push(t2);
                                                        ^~

Yes, you're doing it incorrectly. The similar code will also be incorrect in C/C++, BTW.

        let mut t = Vec::new();
        let mut t2 = Vec::new();
        for x in range(0, 80) {
            for y in range(0, 24) {
                t2.push(Tile::new());
            }
            t.push(t2);
        }

The problem is, you're always pushing into the same t2 in the inner loop and then you're always pushing the same t2 into t . The latter is a violation of ownership semantics, so Rust compiler correctly tells you about using a moved value.

The idiomatic approach is to use iterators and it could look like this:

(0..80).map(|_| (0..24).map(|_| Tile::new()).collect()).collect()

If you need to access indices you can use map() closure arguments:

(0..80).map(|x| (0..24).map(|y| Tile::new(x, y)).collect()).collect()

The compiler should automatically deduce the desired type of collect() result.

Vladimir's answer is really nice, however I have a feeling that the functional style might hide the error here.

You are actually not far from the solution; the issue is simply that you cannot reuse the same t2 at each iteration of the outer loop. The simplest transformation, therefore, is to create t2 inside the outer loop:

impl Level {
    fn new() -> Level {
        Level {
            tiles: {
            let mut t = Vec::new();
            for x in range(0, 80) {
                let mut t2 = Vec::new(); // Moved!
                for y in range(0, 24) {
                    t2.push(Tile::new(x, y));
                }
                t.push(t2);
            }
            t
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM