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

brianokken
Many tasks_proj/tests directories exist in chapters 2, 3, 5 that have tests that use the custom markers smoke and get, which are not decl...
New
yulkin
your book suggests to use Image.toByteData() to convert image to bytes, however I get the following error: "the getter ‘toByteData’ isn’t...
New
jamis
The following is cross-posted from the original Ray Tracer Challenge forum, from a post by garfieldnate. I’m cross-posting it so that the...
New
joepstender
The generated iex result below should list products instead of product for the metadata. (page 67) iex&gt; product = %Product{} %Pento....
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
brian-m-ops
#book-python-testing-with-pytest-second-edition Hi. Thanks for writing the book. I am just learning so this might just of been an issue ...
New
Charles
In general, the book isn’t yet updated for Phoenix version 1.6. On page 18 of the book, the authors indicate that an auto generated of ro...
New
adamwoolhether
Is there any place where we can discuss the solutions to some of the exercises? I can figure most of them out, but am having trouble with...
New
davetron5000
Hello faithful readers! If you have tried to follow along in the book, you are asked to start up the dev environment via dx/build and ar...
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
PragmaticBookshelf
Take your Go skills to the next level by learning how to design, develop, and deploy a distributed service. Start from the bare essential...
New
New
Exadra37
I am thinking in building or buy a desktop computer for programing, both professionally and on my free time, and my choice of OS is Linux...
New
AstonJ
There’s a whole world of custom keycaps out there that I didn’t know existed! Check out all of our Keycaps threads here: https://forum....
New
PragmaticBookshelf
Learn different ways of writing concurrent code in Elixir and increase your application's performance, without sacrificing scalability or...
New
PragmaticBookshelf
Build highly interactive applications without ever leaving Elixir, the way the experts do. Let LiveView take care of performance, scalabi...
New
AstonJ
Continuing the discussion from Thinking about learning Crystal, let’s discuss - I was wondering which languages don’t GC - maybe we can c...
New
PragmaticBookshelf
Develop, deploy, and debug BEAM applications using BEAMOps: a new paradigm that focuses on scalability, fault tolerance, and owning each ...
New
AnfaengerAlex
Hello, I’m a beginner in Android development and I’m facing an issue with my project setup. In my build.gradle.kts file, I have the foll...
New

Sub Categories: