Rust 1.71.0
blog.rust-lang.org
blog.rust-lang.org
One of the current issues with Rust (and a place where C++ is still better) is debugging. Granted, I end up needing to debug Rust code much less often than C++ code, but it would still be nice to actually call rust functions in IntelliJ (and it seems there's limited support in gdb and lldb), and view data-structures like maps opaquely instead of just their complex internals.
What platform / debugger are you on? I'd expect the GDB pretty-printers (which from the article it looks like Rust has been shipping by default for the standard library) to be taking care of this relatively well - e.g. looking at https://github.com/rust-lang/rust/pull/72357 it appears that `HashMap` and `HashSet` have printers.
(Raw C++ data structures without some form of pretty printing are also incomprehensible for normal development - and I'd be surprised if third party libraries were particularly good at shipping pretty printers, though I may be wrong!)
It's quite hard work sometimes - there's a lot in there - but the capabilities available are amazing and it's well worth nosing around.
I also recommend just hitting TAB a lot and seeing what commands are suggested. Then use the "help"command to find out more.
My employer also hosts some articles that digest some knowledge we've gained into a more palatable form: https://undo.io/resources/gdb-watchpoint/
But debuggers like GDB have built in infrastructure for expressing how to display structured, potentially nested data structures - it's built for the purpose and powerful, so it's nice to bring it to bear if you've not already got something equivalent in they program.
The other important thing is that the debugger's pretty printers work on a core file, without needing a live process.
This would make bugs around state in Debug impls much more complicated, for example. This can creep in under a few layers of indirection - ultimately calling a function that gets the value of an interned string would be one example.
It’s gotten much better. Primitive structures which are defined by their fields and builtins like `Vec`, `HashMap`, `String`, are handled good. But structures in library or user code which use “raw” and other non-trivial representations, but provide an opaque safe API, I can only introspect the non-trivial fields.
This annotation is exactly what those custom structures need.
This debugger_visualizer attribute does seem like a really cool capability. It's about embedding debugging scripts for debug tools, in your rust source code; couldn't be more direct! https://doc.rust-lang.org/nightly/reference/attributes/debug...
Though that “only” dates back to 2020 so people with experience older than that would have had a worse experience, and may have ended once bitten twice shy as a result never noticing the improvements.
I'm a non-proud printf() debugger. I understand that if I get use to debugger debugging it'd be in many ways better. The thing is, it's an entirely different paradigm of development, and paradigm shifts can come with significant productivity cost. Of course, learning, growing and getting comfortable with new/different tools is part of being a software engineer.
I also see people who use them all the time but reluctantly - I'd love to know what the difference is.
What if you have to solve a bug outside of the development loop though? E.g. a bug somewhere in the whole system after you've done your development, where you don't have a root cause yet?
Compared to this gdb is just meh. I know, I know, Rust compiles to a nice binary, there's no runtime/vm/interpreter/JIT/intermediate-language/bytecode...
but that's not what I miss in gdb, I miss the discoverability, the visual representation, the whole I from the IDE concept.
No it isn't? IDE debugging works just as well for Rust as it does for C++ in my experience - better even. Yes it sometimes shows raw internals of complex containers instead of the logical structure, but it does for C++ too.
In Rust I can just click "Debug test" next to any test and it will start it in a debugger flawlessly. Zero effort to set up. I've never seen anything close to that in C++.
Of course as someone else said, you need a debugger far far less in Rust than C++.
https://dorotac.eu/posts/debugging_rust/
You can view the data structures opaquely even though the functions are written not in Rust, but in Python.
One of the difficulties is that the Rust compiler will emit different debugging info depending on the compiler version. Perhaps I should look into how gdb deals with that (if at all).
Patches welcome.
But the contents is interesting. Bookmarked.
> $1 = alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: core::ptr::non_null::NonNull<u8> {pointer: 0x5555555abaa0}, _marker: core::marker::PhantomData<u8>}, cap: 3, alloc: alloc::alloc::Global}, len: 3}}
On my browser, everything breaks according to the browser's window size (I set white-space: pre-wrap).
So you don't have to manually evaluate cells, but they are evaluated based on the computations dataflow graph.
Additionally it allows for very interactive visualisations and data input.
(Some precautionary measures would of course be required to avoid infinite recursion and similar. But the same issues already exist in debuggers that allow pretty-printers to eg. run arbitrary python code. To avoid a pathological or buggy formatter breaking either the debugged program or the debugger itself, it might be best to execute the formatter in a separate child process.)
It seems like debug printers should be pure functions but you never know what sort of weird stuff people do.
Lots of devils in the details, but that sounds possible.
The product I work on does pretty much exactly this - we need it because our debugger time travels and we can't allow history to be altered!
I wrote some words here: https://medium.com/time-travel-debugging/calling-functions-i...
But it's basically exactly what you propose. We didn't sandbox the syscalls but you could do that too.
I agree.
> data-structures like maps opaquely instead of just their complex internals.
Works for me:
use std::collections::HashMap;
fn main() {
let mut m = HashMap::<String, u32>::new();
m.insert("a".to_string(), 5);
m.insert("b".to_string(), 3);
println!("Hello, world!");
}
$ rust-gdb target/debug/t1
(gdb) b 7
(gdb) r
(gdb) print(m)
$3 = HashMap(size=2) = {["a"] = 5, ["b"] = 3}> Automatically inherit workspace fields when running `cargo new`/`cargo init`. #12069 [1]
imo I think this is a big quality of life improvement
[0] https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#...
edit: actually, I guess that focuses more on the debugger side, and less on the language side, so not a great answer to your question.
You can even embed that Python script within the shared object using some scary ELF tricks - I was surprised, to say the least, to discover this is supported.
vendor/cargo_metadata, vendor/cargo_metadata-0.14.0, vendor/cargo_metadata-0.14.2, vendor/cargo_metadata-0.15.2
vendor/filetime, vendor/filetime-0.2.16, vendor/filetime-0.2.19, vendor/filetime-0.2.20
vendor/parking_lot_core, vendor/parking_lot_core-0.8.5, vendor/parking_lot_core-0.8.6, vendor/parking_lot_core-0.9.4, vendor/parking_lot_core-0.9.6
etc. Lovely.
fwiw - I learned Rust, was painful at first, but now I love it.
However, the type checking in Rust is fantastic. The confidence in correctness that you have when it is complete is much higher than many other languages.
Use case would probably be a major factor in the decision e.g. picking up rust from scratch to write a basic network service would probably be stupid unless it had very strict requirements known in advance (and even then you might want to write it in go anyway in case that works out or to quickly uncover issues you’d hadn’t predicted).
If you know in advance you have very strict resource constraints however, or you really need the reliability (of extensively encoding things in the typesystem), or you’re writing native extensions (python + rust via pyo3 is great, python + go is something I’d avoid unless the entire solution already existed in Go and I could expose it to python over a pipe), etc… then the investment would likely be more worth it.
Basically, how much return would you expect, Go requires very little investment, Rust a lot more.
Fastest compile times vs slow compile times. Excellent type system vs very basic type system Complex language vs Simple language
If I were to learn a new language it would be rust because I like it's type system.
That being said I use many cli tools written in rust and golang.
> Temporal databases aim to make our programming lives easier around time, by baking time itself into the engine. One major feature of temporal databases is the ability to query the database as of a particular point in time.
For Rust, you can likely encode most rules and transitions between states in the type system itself. It’s a super power in that niche.
Screenshot of the Golang website from ~2010: https://i.imgur.com/AYeUvuE.png
https://learn.microsoft.com/en-us/events/lang-next-2014/pane...
- TinyGo (https://tinygo.org/), which is acknowledged by people in the industry[0][1]
- TamaGo unikernel on USB Armory secure key (https://www.withsecure.com/de/solutions/innovative-security-...)
And then there is the question if writing compilers, assemblers, linkers is systems programming or not.
[0]-https://www.cnx-software.com/2019/08/28/tinygo-go-compiler-f...
[1]-https://twitter.com/ArmSoftwareDev/status/131680481331796787...
Just because you can provide some niche examples where Go is used as a systems programming language doesn't mean it is a general purpose systems programming language.
Putting Go in the same category as C/C++/Rust/Zig is ridiculous.
Ridiculous is being a gatekeeper lacking imagination.
Thankfully for mankind's progress they tend to be a minority, even though they sometimes get into the way.
Yes, it is gatekeeping, back in the 8 and 16 bit home computer days, or even before during the computer revolution, it was anything that could help to develop the whole stack.
From the gatekeepers point of view, Xerox PARC never did any systems programming.
Like I said, I think the main issue here is terminology, but you brushed that aside. From a cursory glance, Xerox PARC definitely did low-level programming so I'm not sure what "the gatekeepers point of view" is supposed to mean.
What is low-level?
Coding in C back in the 8 and 16 bit home computer days when C was useless without piles of compiler extensions beyond K&R C, requiring either inline Assembly or an external Assembler, and even BASIC provided better primitives for hardware access?
On the Go web site there wasn't anything in that direction, for example.
Closest I found by web searches was references to this 2014 post from an analyst company: https://redmonk.com/dberkholz/2014/03/18/go-the-emerging-lan... - in there it's opined that Go has a strong position in cloud infrastructure (but doesn't imply that it's designed or suitable only for that).
Having learned SML / Haskell in the past I've got a bit of a soft spot for languages that are utterly cruel to you with their compilation errors but lead you straight to bug-free code. What I've heard about Rust puts it in this camp.
One thing that does concern me about Rust: when I (now and then) look at what's changing in C++ it feels like a lot of new mechanisms and abstractions are required to address problems created by previous design decisions. I sometimes read about Rust and worry that I might need to embark on a similar journey.
https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-repl...
Can you find the flaw in this statement?
No spec doesn't mean there's nothing keeping rustc honest, it just means there isn't a spec keeping rustc honest.
> Any behavior it exhibits could change tomorrow.
And pigs could fly tomorrow as well. This is not a serious engagement with the ways the Rust developers go to great pains not to change behavior (such as crater runs). In many ways empirical evidence like crater is more valuable than some random document that might or might not be fully obeyed at any given time.
> That they can’t slow down to pin down exactly what defines Rust is also indicative of an immature language.
Rust has slowed down a lot, just not to Drew's liking. And he isn't acknowledging the ways in which the risks of moving fast(er than he would like) are mitigated.
> Safety. Yes, Rust is more safe. I don’t really care. In light of all of these problems, I’ll take my segfaults and buffer overflows.
Please leave the software industry. This is embarrassing.
> I especially refuse to “rewrite it in Rust” - because no matter what, rewriting an entire program from scratch is always going to introduce more bugs than maintaining the C program ever would.
What evidence is there for this position? My contention is that a Rust rewrite would maybe not start that way, but rapidly exceed the C version in quality because it is just a more modern language.
Your concerns regarding "feature creep" are reasonable and would be expected for a language trying to displace C++.
Edit: grammar
I do love Rust, but one complaint is that early design decisions because of my weak understanding are extremely hard to refactor out later on. Poor type choices can end up bleeding into the entire project that become nightmares to improve.
My advice is to use cargo workspaces to break up your project into small self contained modules early so when you will inevitably need to refactor, it will make your life less painful.
You automatically get:
- Minor version upgrade every day.
- Major version upgrade every month.
I really dislike date based versioning unless the major version is year aligned and is truly a big jump forward (like Jetbrains do it).
Otherwise how do you know when a breaking change happens? Or if a version is significant or not? With 1.69.1 I know how .1 relates to .69 and to the 1.
I think this is one of the reasons programmers gets mad at like, Google Chrome or Firefox or whatever about having version number 100, but literally nobody else cares. Version numbers just fundamentally mean something different to engineers who think of "consuming programs" like they do consuming an API, versus a user, who just thinks of it as "Bigger number = more recent = good."
> Otherwise how do you know when a breaking change happens?
Linux is a good illustrative example of how this works. How do you know when a breaking change in Linux happens? The answer is the developers define a stable boundary (userspace) and stick to it, whether or not they use semver, and the other components are out of scope (kernel APIs). They simply have a different criterion for where the line gets drawn. This makes some people mad and some people happy, but it's not exactly new.
Similarly, in most of the calver applications I've seen and used, you typically just don't do huge breaking changes, you just do gradual migrations of existing things, with warnings, rollouts, cutovers, brownouts, etc to control what happens. So the answer is "when do they happen" is "they mostly don't." The release process and guarantees are just different.
Same with "is a version significant or not." How do you know if a version of Linux is significant? You go read the release notes. They come out once every 3 months, so you always know when to look. When you do calver based releases, that question just matters a whole lot less in some sense. Did a feature not get in this time by a hair? It will just get in next time, so there's no need to squeeze yourself or sweat bullets about extending your runway. Sometimes a lot of big features land in one release and sometimes they get held back. It's just how it works.
I don't think calver is very good for actual programmer-facing APIs, necessarily, but it can work e.g. webapps tend to have different versioning schemes and techniques for REST APIs, so clearly semver isn't the only possible technique. It does help introduce some mechanical semantics that can be tool checked, etc. For Rust that's really useful, but for a lot of cases that isn't so relevant.
> You automatically get:
> - Minor version upgrade every day.
> - Major version upgrade every month.
1. Isn't the order normally <major>.<minor>.<patch> ?
2. For sortability the order should if anything follow the ISO 8601 order.
3. Some way to optionally represent the specific rust edition used in the compiler/toolchain version would probably be a good idea.
Therefore, my recommendation would be (if you'd want to actually use this instead of semver):
YYYY.MM.DD(.EDITION)(-PATCH)