Keton

Keton

Hands-on Rust: How do I debug a call chain in Rust?

When running the program in chapter 8, “Implementing Combat”, the printout Health before attack was never printed so I assumed something was wrong in the call chain.

How would I go about debugging this call chain (ecs.entry_mut(*victim).unwrap()...)?

#[system]
#[read_component(WantsToAttack)]
#[read_component(Health)]
pub fn combat(ecs: &mut SubWorld, commands: &mut CommandBuffer) {
    ...
    victims.iter().for_each(|(message, victim)| {
        if let Ok(mut health) = ecs
            .entry_mut(*victim)
            .unwrap()
            .get_component_mut::<Health>()
        {
            println!("Health before attack: {}", health.current);
            ...
        }
        commands.remove(*message);
    });
}

After some comparing of source code, I found out that I had actually written read_component(Health) instead of write_component(Health).

#[system]
#[read_component(WantsToAttack)]
#[write_component(Health)]
pub fn combat(ecs: &mut SubWorld, commands: &mut CommandBuffer) {
    ...
}

This is my first encounter with Rust but I was surprised that this error slipped by the compiler. I expected similar error as if I hade left out some mut

Most Liked

Red

Red

I don’t own this book so I cannot comment on the code, but here are a few tricks I use to debug chained methods and see the intermediate values. I’m using the IntelliJ plugin.

To just see the types at different point, the same techniques can be used - check values in a map, or split the chained method into several intermediate variables.

To take a dummy example:

    let values = vec!["one", "two", "three"];
    let values_str = values.iter()
        .enumerate()
        .map(|(i,s)| {
            format!("{}: {}", i + 1, s)      // <== breakpoint here
        })
        .collect::<Vec<_>>()
        .join(", ");                         // <== breakpoint here
    println!("Result: {}", values_str);

It’s possible to place a breakpoint on the map closure. Make sure to use a block with ‘{ … }’ and that the code is not on the same line. Otherwise, values are not visible:

        .map(|(i,s)| { format!("{}: {}", i + 1, s) }) // <== I cannot see anything

It’s also possible to put breakpoints on the lines after, but on the lines before it’s a bit tricky because it’s an iteration and there is no closure to expose any value. The debugger will stop but will not show anything. Sometimes you may be able to step into the function and catch something from there, but it’s tedious.

One way to look is to insert a dummy identity map. For example if I want to catch the values before the enumerate:

    let values_str = values.iter()
        .map(|x| {
            x                   // <== breakpoint here
        })
        .enumerate()
        .map(|(i,s)| { format!("{}: {}", i + 1, s) })
        .collect::<Vec<_>>()
        .join(", ");

You can even put a println!(), that’s usually easier than looking at the values in the debugger.

Another way is to simply split the calls and store the intermediate result in temporary variables. Note the clone() to avoid losing ownership on the intermediate data:

    let values = vec!["one", "two", "three"];
    let values_str_1 = values.iter()
        .enumerate();
    let debug = values_str_1.clone().collect::<Vec<_>>();
    let values_str = values_str_1                         // <== breakpoint
        .map(|(i,s)| { format!("{}: {}", i + 1, s) })
        .collect::<Vec<_>>()
        .join(", ");
    println!("Result: {}", values_str);

PS: I found out that with that plugin, you had disable the NatVis renderers if you examine the values in the LLDB view, otherwise you get a lot of warnings hindering the view:

herbert

herbert

Author of Hands-on Rust

Hi,

Thanks for reading the book!

Unfortunately, Legion (the ECS) is responsible for the World access (which is what the read_component is handling) - and doesn’t flag that one at compile time. I really wish it did!

The issue can be caught by changing the if let. if let is like a single-case match, so it’s only running the enclosed code if get_component_mut succeeds - and continues on its way if it fails.

get_component_mut returns a Result type. So you have a few options for handling this:

The simplest way, not very specific:

if let Ok(mut health) = ecs.entry_mut(*victim).unwrap().get_component_mut::<Health>() {
} else {
     // Print an error here
}

Using match:

match ecs.entry_mut(*victim).unwrap().get_component_mut::<Health>() {
    Ok(mut health) => { // do the health deduction }
    Err(msg) => { // Handle the error, msg will tell you what went wrong }
}

Or you can choose to crash when things go wrong (surprisingly helpful for debugging):

let mut victim = ecs.entry_mut(*victim).unwrap().get_component_mut::<Health>().unwrap();
// Health deduction code

Hope that helps!

Where Next?

Popular Pragmatic Bookshelf topics Top

Alexandr
Hi everyone! There is an error on the page 71 in the book “Programming machine learning from coding to depp learning” P. Perrotta. You c...
New
rmurray10127
Title: Intuitive Python: docker run… denied error (page 2) Attempted to run the docker command in both CLI and Powershell PS C:\Users\r...
New
jeremyhuiskamp
Title: Web Development with Clojure, Third Edition, vB17.0 (p9) The create table guestbook syntax suggested doesn’t seem to be accepted ...
New
New
s2k
Hi all, currently I wonder how the Tailwind colours work (or don’t work). For example, in app/views/layouts/application.html.erb I have...
New
rainforest
Hi, I’ve got a question about the implementation of PubSub when using a Phoenix.Socket.Transport behaviour rather than channels. Before ...
New
Henrai
Hi, I’m working on the Chapter 8 of the book. After I add add the point_offset, I’m still able to see acne: In the image above, I re...
New
tkhobbes
After some hassle, I was able to finally run bin/setup, now I have started the rails server but I get this error message right when I vis...
New
dtonhofer
@parrt In the context of Chapter 4.3, the grammar Java.g4, meant to parse Java 6 compilation units, no longer passes ANTLR (currently 4....
New
New

Other popular topics Top

AstonJ
If it’s a mechanical keyboard, which switches do you have? Would you recommend it? Why? What will your next keyboard be? Pics always w...
New
AstonJ
You might be thinking we should just ask who’s not using VSCode :joy: however there are some new additions in the space that might give V...
New
AstonJ
I’ve been hearing quite a lot of comments relating to the sound of a keyboard, with one of the most desirable of these called ‘thock’, he...
New
AstonJ
This looks like a stunning keycap set :orange_heart: A LEGENDARY KEYBOARD LIVES ON When you bought an Apple Macintosh computer in the e...
New
AstonJ
Biggest jackpot ever apparently! :upside_down_face: I don’t (usually) gamble/play the lottery, but working on a program to predict the...
New
AstonJ
Was just curious to see if any were around, found this one: I got 51/100: Not sure if it was meant to buy I am sure at times the b...
New
PragmaticBookshelf
Author Spotlight Jamis Buck @jamis This month, we have the pleasure of spotlighting author Jamis Buck, who has written Mazes for Prog...
New
DevotionGeo
I have always used antique keyboards like Cherry MX 1800 or Cherry MX 8100 and almost always have modified the switches in some way, like...
New
First poster: bot
zig/http.zig at 7cf2cbb33ef34c1d211135f56d30fe23b6cacd42 · ziglang/zig. General-purpose programming language and toolchain for maintaini...
New
First poster: AstonJ
Jan | Rethink the Computer. Jan turns your computer into an AI machine by running LLMs locally on your computer. It’s a privacy-focus, l...
New

Sub Categories: