roguelike-tutorial
roguelike-tutorial copied to clipboard
Error in part 2b: index out of bounds: the len is 50 but the index is 50
I'm getting an error when trying to compile. My code is basically part 2b, here's a gist of my code. I even ran ediff on it and made sure that it's roughly the same as your version but in a slightly different order.
Here's the error with a full backtrace:
~/Documents/projects/roguelike:no-branch*? λ RUST_BACKTRACE=1 cargo run --release
Finished release [optimized] target(s) in 0.0 secs
Running `target/release/roguelike`
24 bits font.
key color : 0 0 0
24bits greyscale font. converting to 32bits
Using SDL renderer...
thread 'main' panicked at 'index out of bounds: the len is 50 but the index is 50', /checkout/src/liballoc/vec.rs:1564:14
stack backtrace:
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: std::sys_common::backtrace::_print
at /checkout/src/libstd/sys_common/backtrace.rs:71
2: std::panicking::default_hook::{{closure}}
at /checkout/src/libstd/sys_common/backtrace.rs:60
at /checkout/src/libstd/panicking.rs:380
3: std::panicking::default_hook
at /checkout/src/libstd/panicking.rs:396
4: std::panicking::rust_panic_with_hook
at /checkout/src/libstd/panicking.rs:611
5: std::panicking::begin_panic_new
at /checkout/src/libstd/panicking.rs:553
6: std::panicking::begin_panic_fmt
at /checkout/src/libstd/panicking.rs:521
7: rust_begin_unwind
at /checkout/src/libstd/panicking.rs:497
8: core::panicking::panic_fmt
at /checkout/src/libcore/panicking.rs:92
9: core::panicking::panic_bounds_check
at /checkout/src/libcore/panicking.rs:68
10: roguelike::main
11: __rust_maybe_catch_panic
at /checkout/src/libpanic_unwind/lib.rs:98
12: std::rt::lang_start
at /checkout/src/libstd/panicking.rs:458
at /checkout/src/libstd/panic.rs:361
at /checkout/src/libstd/rt.rs:59
13: __libc_start_main
14: _start
It seems to be a buffer overflow of some sorts, but I don't really know how it's possible.
Did I do something wrong?
Unrelated:
I also tried to implement the continuous vector version of the map (thought that it may solve the issue, and also that it could be a good exercise), but that required me to either write C-style code full of "global" functions (which seemed kind of ugly) or turn Map into a struct, which is overengineering things and also doesn't allow me to elegantly work with the map without getters and setters (they seem to be frowned upon in Rust). All in all this solution wasn't very elegant so I gave up on it.
Hi!
First, a general debugging tip: if you run it with cargo run (without the --release option), it will be much slower, but the stack backtrace on panic will show the actual functions that were called instead of Rust's internals. You can do the same thing with --release if you put this in your Cargo.toml:
[profile.release]
debug = false
I think this line is the problem:
https://gist.github.com/omrisim210/3113be80c543bdd4725be588bc529756#file-main-rs-L112
Your MAP_WIDTH is set to 50 (the tutorial has it at 80), but accessing a Vec in Rust is 0-indexed, so by writing map[50], you're asking for the 51th element. The map is only 50 tiles wide, hence the panic. It's not a buffer overflow, Rust just does a bounds check to make sure you're not reading garbage memory.
Switching to a contiguous Vec would not solve this (it would just put the wall in a wrong position), but it a common (and generally more performant) way of handling maps like this, so I do encourage you to try it :-).
I don't see your comment as over-engineering: wrapping an internal representation (the contiguous Vec) in a struct with controlled access is perfectly normal. I do literally the same thing in my roguelike:
Here's my map struct:
https://github.com/tomassedovic/dose-response/blob/master/src/level.rs#L74-L78
And here's how to read and write to it:
https://github.com/tomassedovic/dose-response/blob/master/src/level.rs#L114-L122
Mine's more complicated because I have to translate between world and level coordinates (my world is infinite and each map is a small chunk of it), but the basic idea is the same: contiguous Vec + accessors.
If you want to get fancy, you can even implement the (std::ops::Index)[https://doc.rust-lang.org/std/ops/trait.Index.html] trait to get the same map[x][y] syntax :-).
Regarding getters and setters in Rust, you may have misunderstood a little. They are perfectly acceptable in cases where you want to hide the implementation or to check the inputs, etc. What is frowned upon is doing this where a public struct would be sufficient.
I.e. instead of:
pub struct Point {
x: i32,
y: i32,
}
impl Point {
pub fn new(x: i32, y: i32) -> Self {
Point{ x, y}
}
pub fn x(&self) -> i32 {
self.x
}
pub fn y(&self) -> i32 {
self.y
}
}
(I believe this style of programming is common in some OOP languages/circles)
We generally prefer this:
pub struct Point {
pub x: i32,
pub y: i32,
}
Thank you for the detailed answer, I really appreciate it! I'll get coding and see if I can implement what I wanted.
On Oct 3, 2017 11:20, "Tomas Sedovic" [email protected] wrote:
Hi!
First, a general debugging tip: if you run it with cargo run (without the --release option), it will be much slower, but the stack backtrace on panic will show the actual functions that were called instead of Rust's internals. You can do the same thing with --release if you put this in your Cargo.toml:
[profile.release] debug = false
I think this line is the problem:
https://gist.github.com/omrisim210/3113be80c543bdd4725be588bc5297 56#file-main-rs-L112
Your MAP_WIDTH is set to 50 (the tutorial has it at 80), but accessing a Vec in Rust is 0-indexed, so by writing map[50], you're asking for the 51th element. The map is only 50 tiles wide, hence the panic. It's not a buffer overflow, Rust just does a bounds check to make sure you're not reading garbage memory.
Switching to a contiguous Vec would not solve this (it would just put the wall in a wrong position), but it a common (and generally more performant) way of handling maps like this, so I do encourage you to try it :-).
I don't see your comment as over-engineering: wrapping an internal representation (the contiguous Vec) in a struct with controlled access is perfectly normal. I do literally the same thing in my roguelike:
Here's my map struct:
https://github.com/tomassedovic/dose-response/ blob/master/src/level.rs#L74-L78
And here's how to read and write to it:
https://github.com/tomassedovic/dose-response/ blob/master/src/level.rs#L114-L122
Mine's more complicated because I have to translate between world and level coordinates (my world is infinite and each map is a small chunk of it), but the basic idea is the same: contiguous Vec + accessors.
If you want to get fancy, you can even implement the (std::ops::Index)[ https://doc.rust-lang.org/std/ops/trait.Index.html] trait to get the same map[x][y] syntax :-).
Regarding getters and setters in Rust, you may have misunderstood a little. They are perfectly acceptable in cases where you want to hide the implementation or to check the inputs, etc. What is frowned upon is doing this where a public struct would be sufficient.
I.e. instead of:
pub struct Point { x: i32, y: i32, } impl Point { pub fn new(x: i32, y: i32) -> Self { Point{ x, y} }
pub fn x(&self) -> i32 { self.x } pub fn y(&self) -> i32 { self.y }}
(I believe this style of programming is common in some OOP languages/circles)
We generally prefer this:
pub struct Point { pub x: i32, pub y: i32, }
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tomassedovic/roguelike-tutorial/issues/23#issuecomment-333773213, or mute the thread https://github.com/notifications/unsubscribe-auth/AM1QKVAxCR6960oyGLynpZimQPp3dNAgks5soe5qgaJpZM4Pp0I_ .
No worries! If you're unclear about something, feel free to ask me here or on IRC (I'm shadower, you can find me in #rust and #rust-gamedev in Mozilla's server).