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

jimmykiang
This test is broken right out of the box… — FAIL: TestAgent (7.82s) agent_test.go:77: Error Trace: agent_test.go:77 agent_test.go:...
New
jimschubert
In Chapter 3, the source for index introduces Config on page 31, followed by more code including tests; Config isn’t introduced until pag...
New
Mmm
Hi, build fails on: bracket-lib = “~0.8.1” when running on Mac Mini M1 Rust version 1.5.0: Compiling winit v0.22.2 error[E0308]: mi...
New
AleksandrKudashkin
On the page xv there is an instruction to run bin/setup from the main folder. I downloaded the source code today (12/03/21) and can’t see...
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
Chrichton
Dear Sophie. I tried to do the “Authorization” exercise and have two questions: When trying to plug in an email-service, I found the ...
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
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
kolossal
Hi, I need some help, I’m new to rust and was learning through your book. but I got stuck at the last stage of distribution. Whenever I t...
New
redconfetti
Docker-Machine became part of the Docker Toolbox, which was deprecated in 2020, long after Docker Desktop supported Docker Engine nativel...
New

Other popular topics Top

PragmaticBookshelf
Free and open source software is the default choice for the technologies that run our world, and it’s built and maintained by people like...
New
PragmaticBookshelf
Ruby, Io, Prolog, Scala, Erlang, Clojure, Haskell. With Seven Languages in Seven Weeks, by Bruce A. Tate, you’ll go beyond the syntax—and...
New
AstonJ
Just done a fresh install of macOS Big Sur and on installing Erlang I am getting: asdf install erlang 23.1.2 Configure failed. checking ...
New
Exadra37
I am asking for any distro that only has the bare-bones to be able to get a shell in the server and then just install the packages as we ...
New
AstonJ
In case anyone else is wondering why Ruby 3 doesn’t show when you do asdf list-all ruby :man_facepalming: do this first: asdf plugin-upd...
New
AstonJ
If you want a quick and easy way to block any website on your Mac using Little Snitch simply… File &gt; New Rule: And select Deny, O...
New
PragmaticBookshelf
Author Spotlight: Peter Ullrich @PJUllrich Data is at the core of every business, but it is useless if nobody can access and analyze ...
New
New
PragmaticBookshelf
Build modern server-driven web applications using htmx. Whatever programming language you use, you’ll write less (and cleaner) code. ...
New
PragmaticBookshelf
Fight complexity and reclaim the original spirit of agility by learning to simplify how you develop software. The result: a more humane a...
New

Sub Categories: