Why there is less thing I can do in Rust debugging than in Python/C++ debugging?

As is known by Python users, one can do many things when using ipdb to debug python.

But I found that when i am using gdb or some Rust-IDEs to debug Rust, I can do far less things on breakpoints(see examples below)which may certainly bring lots of inconvenience to Rust coding and debugging.

I want to know the reason(My misoperation or some theoretical reasons?)

  • examples:
      1. execute methods on breakpoints
      • Python(ipdb):no problem
      • C++(visual Studio 2022):methods can be executed once used in codes
      • Rust(RustRover/gdb):debugger raise an err “no field named xxx” even if the method is used in codes:
    • 2)execute functions on breakpoints

      • Python:no problem
      • C++: no problem
      • Rust: debugger raise an err “could not find item”

I also raise a question about simliar problem on Stackoverflow: Edit - Stack Overflow

  • Python has an interpreter and run-time information on all types, so it is easy for a debugger to just call the interpreter to run some code.
  • C++ has a very long history and many people have worked on implementing debugger expression evaluation support. (And even then, if you write code that is not so simple, they will fail, I expect — it is not a full C++ implementation.)
  • Rust has neither the advantage of an interpreter nor the advantage of effort already spent on tools.
13 Likes

Thanks for your replying~

So,May I conclude that:

  • The debugging difference between Python and C++/Rust are almost equal to the debugging difference between interpreted languages and compiled languages
    • When I am using interpreted languages, surely there is more convenience while debugging.
  • But The debugging difference between C++ and Rust is from the current poor Rust support of debug tools
    • Maybe in the future Rust codes(even c++ codes) can be debugged more conveniently, but not now.

On the flip side, I can not write C++ w/o valgrind + gdb, but in Rust, println! + logging has been enough for everything I need. I.e. it is not clear to me effort spent on "better debugging" is better than spent on "reduce need for debugger / catch more at compile time"

9 Likes

Yeah I think that is the advantage of Rust, one can spend less time on manually solving memory problems which is unavoidable in other languages.

But in my daily works, memory management is not the main problem, the challenge is mainly about writing complicated codes to achieve various complex functions(or effect? Pls ignore my poor English)

So i ...just cannot endure the inconvenience of using "println!"/"debug!" and then compiling once to debug. Each time I do that, I miss the convenience of C++ debugging and Python ipdb, badly

Maybe Rust is not suitable for research works? I think...

There is GitHub - evcxr/evcxr , but I do not think "repl / interactive" driven development is the Rust way -- and I used to use Clojure before Rust. :slight_smile:

3 Likes

Thanks, i will immediately try it out

This isn't what you asked for, but I think this might be useful to you:

As a Python developer I can understand why you emphasise a great debugging experience when experimenting in Rust, because in any sufficiently large Python application, debugging is something we have to do a lot - because the debugger is the only place were we get to find out what is really happening behind the "it's duck typed, don't worry about it" facade and do things correctly.

In Rust, thanks to its strong type system, this is almost never necessary, even more so when not using unsafe, because so many mistakes are caught by the compiler. This is in extreme contrast to Python, where almost nothing is caught by the compiler.

Before you invest a lot of time and energy into replicating a Python-like debugging experience, try to write idiomatic, type-safe Rust code. You will notice that you almost never have to debug. I've been writing Rust professionally for a year now and I've yet to need a debugger.

12 Likes

Yeah I think you have to differentiate what kind of code you are writing. If you are writing for example a numerical algorithm and it's not stable for some input then you want to check what's actually happening in the function. Types don't really help you in such a case.
On the other hand if, for example, you want to reason about how data flows through a complex application, then rusts type and ownership model are great at making that very clear at compile time, no debugger needed.

9 Likes

Rust wants to know about how your API behaves and then, in turn, it helps you to use it correctly.

Thus it's not suitable for “combinatoric programming” where you combine random pieces of code and see if they work or not.

Some use “combinatoric programming” for production code. These should be fired, there are no need for that all all.

Some use “combinatoric programming” for research. They just need some other language.

Even if you would convince compiler to change value of some variable very often you couldn't change the program using that approach because language rules would, very much, prevent similar changes in code!

6 Likes

Well again, it depends on what is meant by research software. Writing controll software for a vacuum chamber for an experiment is also research software and benefits greatly from the UB freeness of rust.

High performance applications in research are typically written in C++ and I believe even here rust is a great contender. But if you talk about data analysis then I'd probably agree. Go with python or something similar.

That's “research in software” vs “software for research in something else”.

If you have something outside of your software that dictates the rules (doesn't matter whether it's “vacuum chamber” or “server on the internet”) then you can embed these rules into types, then into Rust's API and benefit greatly from compiler help.

But if you have to idea about the rules that your software have to follow, you are playing with them and want the to dynamically “mangle your code and see how it would respond” in runtime then Rust's rules about how programs should work would become a hindrance for you.

I would even go as far as to say that precisely what makes Rust great is also makes debuggers hard to create.

The topic started from attempt to call unwrap which failed. But what if compiler understood it and then type which was used would have been non-Copy? What should happen after that? You may only call unwrap from that position once and then variable is destroyed. But you, presumably, want the program to continue. And it would continue with some invariants that were supposed to be intact broken!

This would, presumably, mean that “Rust-in-debagger” would need to have separate rules, separate reference and so on.

Lots and lots of work to enable something which wouldn't be great, anyway.

You could never turn Rust into Python, not matter how hard would you try.

If you want to experiment with your code then Rust is not for you, period. If you want to do experiments on top of your code then situation is very much different, sure.

2 Likes

Note that if you use something like log crate you can remove logging at compile time with features and also disable debug printing (but not checking whether debug printing is enabled) at runtime without going to your code to remove debug prints (you will still have to add them though). This way for each logging statement there are three variants:

  1. All statements of this logging level were disabled at compile time. You will pay no cost for its presence except for small increase in compile time.
  2. This statement was disabled at runtime, but it will still check whether it was enabled. I am not sure how performant this check is.
  3. This statement was enabled at runtime and you will pay full cost for that, including checking whether it was enabled.
3 Likes

Thanks a lot for explanation and advices~

Maybe in this case we dont need seperate rules? When one is going to execute "a.unwrap()" in breakpoints, he is surely expected to have the expectation that after his debugging in breakpoints, the remain codes(especially codes about 'a') wont always run as his wish before.

It's just like when using python ipdb to debug this:

a = 1
ipdb.set_trace()
b = a + 1

when i execute "a = None" in the breakpoint and continue running, it is mean for me to demand that python will execute "b = a + 1" correctly like my debugging never happened.

So i'd like to consider that maybe the reason causes the failure of "unwrap() during debug" is not the rules of Rust?

But still, I got that " what makes Rust great is also makes debuggers hard to create", thanks.

From what you describe here you might want to try F#, which you can interpret as a strongly-typed functional version of Python. It's ML-based, so some of the syntax and concepts are similar to Rust -let, immutable by default, no null, pattern matching. It can be used interactively and also has Notebook support.

F# is good for Data, AI and scientific computing. (Comparison with Rust in this video.)

You could rapidly prototype/experiment in F# and then port your stable version to Rust if necessary.

2 Likes

Thank you for your advice~ I'll see if I can have a try~

1 Like

I come from Python too, and ipdb has unmatched capabilities. Regarding Rust, I'd say

  1. You can see the equivalent of d.unwrap() in VS Code LLDB plugin and even match it in a debug condition expression. The debugger is kinda clumsy, and still has limitations like not running methods, but data is viewable.

(You should read its manual on how to access fields.)

  1. I split big pieces of code into smaller callable functions to test them separately for normal inputs and edge cases. Tests are super easy to create. You may need some simple syntactic macros to ease fixtures. This does not eliminate the need for debugging, but does get rid of lots of bugs.
// your main module content

#[cfg(test)]
mod my_tests {
    use super::*; // import all from the module
    #[test]
    fn my_test1() {
        // test your objects
    }
}

// also have some debugging code for tests inside your normal code:
fn my_normal_func() {
    // do something
    if cfg!(test) {
        // dump some data in files, etc.
    }
}
2 Likes

There was a promising rust debugger attempt a few years back: headcrab

It's a big project, and people have struggled to find time/motivation... I can only imagine how fiendishly complicated it all gets at the low level.

I don't think it got close to the level of C++/python debuggers. But it does show there's some desire for rust debugging out there, but perhaps not enough to actually finish a debugger.

1 Like

I've just stumbled upon another rust debugger project! https://firedbg.sea-ql.org/

I have to say, I'm excited by it. It looks really impressive, and I'm keen to give it a go. Looks like it uses Lldb but has it's own rust specific visualisations. There's a VS code extension and it's open source