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

johnp
Running the examples in chapter 5 c under pytest 5.4.1 causes an AttributeError: ‘module’ object has no attribute ‘config’. In particula...
New
swlaschin
The book has the same “Problem space/Solution space” diagram on page 18 as is on page 17. The correct Problem/Solution space diagrams ar...
New
curtosis
Running mix deps.get in the sensor_hub directory fails with the following error: ** (Mix) No SSH public keys found in ~/.ssh. An ssh aut...
New
jskubick
I’m running Android Studio “Arctic Fox” 2020.3.1 Patch 2, and I’m embarrassed to admit that I only made it to page 8 before running into ...
New
adamwoolhether
I’m not quite sure what’s going on here, but I’m unable to have to containers successfully complete the Readiness/Liveness checks. I’m im...
New
jskubick
I found an issue in Chapter 7 regarding android:backgroundTint vs app:backgroundTint. How to replicate: load chapter-7 from zipfile i...
New
a.zampa
@mfazio23 I’m following the indications of the book and arriver ad chapter 10, but the app cannot be compiled due to an error in the Bas...
New
New
bjnord
Hello @herbert ! Trying to get the very first “Hello, Bracket Terminal!" example to run (p. 53). I develop on an Amazon EC2 instance runn...
New
roadbike
From page 13: On Python 3.7, you can install the libraries with pip by running these commands inside a Python venv using Visual Studio ...
New

Other popular topics Top

Devtalk
Reading something? Working on something? Planning something? Changing jobs even!? If you’re up for sharing, please let us know what you’...
1033 17470 383
New
AstonJ
We have a thread about the keyboards we have, but what about nice keyboards we come across that we want? If you have seen any that look n...
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
PragmaticBookshelf
Tailwind CSS is an exciting new CSS framework that allows you to design your site by composing simple utility classes to create complex e...
New
AstonJ
If you are experiencing Rails console using 100% CPU on your dev machine, then updating your development and test gems might fix the issu...
New
PragmaticBookshelf
Learn different ways of writing concurrent code in Elixir and increase your application's performance, without sacrificing scalability or...
New
New
New
PragmaticBookshelf
Author Spotlight: Karl Stolley @karlstolley Logic! Rhetoric! Prag! Wow, what a combination. In this spotlight, we sit down with Karl ...
New
AstonJ
Curious what kind of results others are getting, I think actually prefer the 7B model to the 32B model, not only is it faster but the qua...
New

Sub Categories: