Debugging Unsafe Rust

Hi, I want to learn Rust because it seems to be as fast as c++ while being safe at the same time, but if I were to use unsafe rust does it dynamically tell me where my code is crashing or are there any crates that would help me to identify which part of the code is causing the crash, for example if the crash was caused by corrupted memory or a data race?

And then it would be cool if you could turn off these dynamic checks when you decided that you've tested your unsafe code enough.
Thanks.

1 Like

Generally you can use the same tools to debug Rust as you would for C++. I understand gdb, lldb and Valgrind all work fine on Rust programs.

Be aware that "crashing" is something of a best case scenario for memory unsafety. Something that crashes reliably is much easier to deal with than something that just occasionally gives bogus answers and you don't know why.

5 Likes

The sole purpose of unsafe is to allow you to assume some of the compiler's proof obligations in cases that are too complex for the compiler to prove on its own. unsafe means that you promise the compiler that you have followed all of Rust's rules everywhere within your unsafe code. If you fail to do that you have no right to expect correct compilation, because the hyper-optimizing compiler back-end, LLVM, may trash your code if you failed to meet its strict input requirements anywhere within the program. Testing won't help; there's no way to prove that LLVM didn't somehow produce code that malfunctions under some conditions, or that it won't do so the next time you make changes and recompile, or when you use a newer version of the compiler.

While mostly you're on your own with getting unsafe right, more and more of it is checkable if you can run your code in https://github.com/rust-lang/miri#miri--

But, as always with testing, that can only find problems in scenarios you thought about. It's always possible that there's something you didn't test that's completely busted.

2 Likes

No.

The purpose of unsafe code is to let you remove all checks and safety layers that stand between the developer and being able to write C code using Rust syntax (manipulating raw pointers, type punning, shared mutation without enforced synchronisation, data races, etc.).

If you want the safety checks then you'll use safe constructs that enforce safety (RefCell, Rc, Vec<T>, etc.).

The main reason we don't let people just "turn the checks off" is because a lot of safety is enforced statically at compile time (e.g. the type system enforces that you can't have multiple mutable borrows), so there's no "flag" to remove, per-se.

The compiler exposes debug_assertions to let you do conditional compilation based on whether the user wants a debug or release build, but that's mainly intended for runtime assertions during development like integer overflow checks or sanity checks.

It would be frowned upon by the Rust community to change the statically enforced contracts in your API based on this (e.g. to remove lifetime annotations or switch from RefCell to UnsafeCell). Not to mention a maintenance nightmare.