Rust After the Honeymoon
dtrace.org
dtrace.org
What a wonderful post to be able to read and not have to wonder how much of it will hold up given a little time.
When I start playing with something, once the number of papercuts I'm tracking in my head gets above 2–3, then I crack open a fresh notes.markdown file adjacent to the source repo (if there is one) and start recording all my thoughts in the vein of[1], including everything from outright bugs to opportunities for UI improvement. If the project has reasonable bugtracking infrastructure, I'll file them—or for small projects, I'll email the author along with any patches—but since most projects don't, I mostly don't end up doing anything with them. I've often thought that it would be nice if this exercise of "Let's Critique" became a thing, though. If nothing else, it could provide fuel for folks' blogs that have been lying dormant since that one post from 2017.
1. https://pointersgonewild.com/2019/11/02/they-might-never-tel...
Otherwise reviews are pointless things like the packaging quality and shipment speed.
I agree with you though that a lot of reviews are useless fluff like shipping speed and packaging, and that there should be a better way to evaluate products.
I generally take breathless coverage of how brilliant some new language (or tech stack) is with a large pinch of salt. The foibles and pitfalls of modern languages are often hard to spot until people have code-bases that are at least a couple of years old. I think the good stuff is generally easier to see at first, while the problems (which are often consequences/side-effects of the good aspects) generally take longer to appear.
Programming languages seem to follow a variation of the Gartner Hype Cycle, with the twist that just after you get to the Plateau of Productivity and lots of people are quietly getting stuff done with it everyone starts to declare that "language X is dead/dying".
Elixir is great at what it does, but I wouldn't use it for building a CLI tool or something like that.
After thirty-something years of programming I've seen numerous languages (or model) touted as the one true language that will save us all.
Unless a feature of the language is game-changing for you. This might mean it solves a problem that would be very hard to solve otherwise or it might mean it looks very interesting to you.
Which I think is good advice with anything that requires you to be an early adoptor.
To quote Grissom from CSI: I've learned that sometimes you can go faster by going slow ;-)
> I think this profession is hard enough as it is.
Exactly, I think so too. Don't think I need another source of annoyance like spending days on digging at obscure segfaults and data races caused by logical mutability mistakes.
Another dimension to it is that the borrows checker rules also apply in most other languages if you want to write valid code. They are just not enforced and you have to rely on your own discipline instead (which humans are bad at).
The problem with unsafe programming in the traditional C style (with its heavy reliance on shared, mutable, possibly aliased pointers) is that it's inherently non-compositional: proving safety or correctness of such a piece of code is a 'global' problem that cannot be meaningfully decomposed or modularized. That's why Rust's choice of explicitly restricting these features to "unsafe" blocks feels intuitively right - the "safe" part of Rust is essentially the part that can be worked with in a highly modular way, where safety is checked 'locally' via a type system. Future versions of Rust may well make some currently-unsafe features slightly easier to use, perhaps even more idiomatic in a way that might appeal to C developers, but some inherent constraints will remain.
Yes, ARM processors are used more and more, but there are really A LOT of embedded systems out there, which are still, for example, 8 bit. As an embedded dev, who is using pretty much everything from 8 to 64 bit, the kind of memory safety errors you talk about I have yet to see with on anything based on (say) a 8051 or PIC. On a system without OS and without dynamic memory allocation, the borrow checker does not buy you much. It's a different story with an ARM processor and an OS (real-time or not does not matter).
Of course Rust has a more powerful type system, but my experience is, that the preferences for a C type type system dominate the embedded world. Not because it is better or simpler, but because most embedded devs actually like low level. They are simply not interested in type driven development (yes it's just their personal taste) and its benefits.
Or let me say it like this. I see two kinds of embedded devs:
- Those who abstract way the problem ("It's all just a big char array.", "It's all just ints.", "It's all just data structures").
- Those who abstract away the machine ("It's all just objects.", "It's all just types.").
Right now, my observations tell me that the first group is still dominating. Time will tell if this is going to change.
As an embedded dev one of the things I love about Rust is the ability to use the type system to enforce the machine's invariants. Some register has a bunch of bit fields? Break it up using a struct, and unlike C's struct bitfield notation you don't have to shift the values around every time.
Rust forces you to check all enum variants, not just in switch statements like C (and that only with the -Wswitch-enum or equivalent warning) and disallows any unlisted value (there's no "default" for an enum).
Those things alone probably aren't enough to switch, but as a machine-focused embedded dev they're certainly nice to have.
But over the time this argument loose fairness in some direction. Today, in 2020, I would feel awful if in my meetings I argued that creating unit tests is meaningless, as this will point bugs in my code that I need to fight with. Or that QA is a bunch of mean people that do not understand how to use my library and only want to point to "bugs" that is more work to me to "fix".
IMHO the borrow checker _is_ this test that I do not want to write, or this QA engineer that exercise the code to find a corner case when I am causing a memory corruption in my not-perfect work.
My approach of learning any language with "difficult syntax" is to treat those as language features rather than a burden.
It is the language bridging the coder to the complexity under the hood (the computer). This comes from Rust's nature of being a low-level PL, and it is really awesome to be able to abstract those minute details so that one can write Rust and "look, it's not that different from JavaScript"
Another analogy would be `gerund` in English. English has the `gerund` feature for its speakers to nounify a verb. Other languages don't have that, some even use the same symbols for both the verb and the noun form.
Both Rust's "difficult syntax" and English's gerund needs only one-time learning session. Once you're fluent on it the sentences come out on their own.
Also many processor manufacturers have hundreds of different models with similar but different port mappings. They often supply C-header files which helps making your software compatible with numerous different processor types. So even while Rust has a macro system, it would need to be adapted by those manufacturers or C will likely stay the more convenient choice for the time being.
https://www.misra.org.uk/Activities/MISRAC/tabid/171/Default...
https://www.autosar.org/fileadmin/user_upload/standards/adap...
https://en.cppreference.com/w/cpp/freestanding
Certifications that Rust has yet to achieve.
Most of the embedded platforms of old will be 16 bit, and as soon as somebody upgrades them to 32 bit (or more), the problem will most likely vanish. Then the next problem arises. Most embedded platforms are governed by strict regulations (mobile phones, medical, automotive, etc) and requires thorough testing. Even if you upgrade your hardware (and if the current, cheap, 16 bit hardware works, why bother ?), you do not want the challenge of rewriting 20-30 years of code in a new language.
In many cases you're looking at 30+ years of development by 50-100 developers, and while the code itself probably hasn't grown by a factor 30, chances are that the system evolved from a small system where core business was understood by "everybody" into a behemoth where only a few developers/architects actually understand everything , and everybody else just understands what their little corner does.
Many people mistakenly think embedded means small, which is often far from the truth. I used to make sortation devices, and the total codebase for a single sortation device would often reach 1GB of C source code. Around 750MB was "common code", but the last 250MB would be customer customization.
A GSM handset in the late 90's had about 350MB C source code, with the majority of it being protocols (GSM, Edge, Bluetooh, SMS/EMS/MMS, WAP). The lower level ones of those have mostly been replaced by hardware these days, only to have the UI take up much more code.
It's essentially the same problem that keeps COBOL in this world. Newer and (probably) better languages exist, but COBOL has a massive codebase, and while "edge" systems are easy to replace, at some point you're left with "core business" which has been running as a patchwork of COBOL programs for 50 years. There's no easy way to do it, and you need to rewrite everything.
So I perfectly know what is doable when one actually cares on how to do stuff properly.
And then there are those Jason demos on how to target C64 with C++.
This sounds completely bonkers, aren't you including a bunch of static assets like images or whatever else?
I dress slowly because I’m in a rush.
"Time is money".
;-).
Edit: Corrected typo.
* Rust's no_std means you can run bare metal with less memory overhead than even newlib based c
* no_std also makes it easier to port to new platforms, as nonportable parts are isolated out of the std lib
* algebraic data types and in particular enums that can carry disjoint associated data reduce the number of error prone magic constants and casts in your code, as well as catching all missing switch cases
* unsafe makes it very easy for you to audit where data races or memory errors are possible, instead of needing to audit an entire c codebase
* The borrow check makes you work more, but in many cases this is justified by it leading you to a less brittle design
The examples, particularly on cliff's blog post, make pretty convincing arguments oo these points imo.
I also wouldn't discount EEs so much. There's plenty of complexity to Verilog and all the other tools they use.
No. no_std means that all standard library functions that involve allocation aren't available. Things like std::collections::{Vec, HashMap} etc. You have access to std::core (https://doc.rust-lang.org/core/index.html), which is pretty useful.
* unsafe makes it very easy for you to audit where data races or memory errors are possible, instead of needing to audit an entire c codebase
I have seen this argument a couple of times before. But I can't make myself buy it.
Because I always have to think about embedded systems in the car industry. Look at this list:https://betterembsw.blogspot.com/2018/09/potentially-deadly-...
The argument is basically "the possible fail places are easily grepable", right? I said in another comment in this thread (jokingly) "time is money". It seems for the devs in the car industry this saying is of utter most importance.
So how does Rust prevent something like this throughout the code base (and those are BIG code bases):
"LOL. I need to get this done. unsafe { ... } "
In some sense, it doesn't. But in another sense, you can't just throw unsafe around things. Unsafe doesn't turn off checks. Unsafe adds new unchecked constructs. This means you can't have written a bunch of code, run into errors, and just toss unsafe {} around it. You'd have to actually re-write it with unsafe constructs in the first place.
You can very much do that, if you want. But it's not a trivial escape hatch.
My point was that, with "unsafe", Rust provides a technical construct which allows to carry over some negative development styles, possible in the C(++) world, without friction (if wanted). And from the list I linked, there seems to be a lot of momentum for this kind of behaviour in certain industries.
You'd have to actually re-write it with unsafe constructs in the first place.
This is what I think would happen, and tried to describe. Someone trying to implement a feature, failing with safe mode and resorting to "unsafe" from scratch, just to get it done.So it stays the way it is, i.e. I can't buy the "possible fail places are easily grepable" argument.
Because embedded software is increasingly networked (BLE, TCP/IP, ZigBee, etc.) and you can continue writing your communication stacks in C and having memory corruption security vulnerabilities or you can suck it up and try something else for once.
> Also in this world all major HALs and SDKs are written in C. The toolchains usually are very fixed, since doing embedded systems is hard...
1) The embedded world has effectively converged on ARM. This means that the toolchain is whatever ARM shoves out and HALs and SDKs will comply or get no traction.
It also means that you can use actual, real software tooling (VSCode, Meson, Ninja, etc.) instead of "Yet Another Broken Vendor IDE". It's soooo compelling that I personally can run rings around some vendor teams. To be fair--this is NOT limited to Rust. VSCode and its ecosystem enables even C programmers to be stupidly more productive. However, the embedded Rust folks seem to rattle the VSCode folks cage quite a bit more than just plain embedded C folks--this keeps the VSCode guys quite a bit more honest about cross-platform support.
2) Anything RISC-V is in flux, and folks like Bryan Cantrill are leading the charge so the toolchain will be forced to accommodate more than "just C".
3) Even if Rust isn't the answer, you should root for its tooling to break the C hegemony. If Rust finally forces C tooling to acknowledge that "Hey, just maybe we should think about playing nicely in the sandbox and have some useful API's instead of telling everybody to cope or leave because we're the 500lb gorilla.", the successor to Rust will have a vastly easier time.
For instance, a developer writing the software for the sensors of a forklift has enough to worry about to not crushing the skull of someone passing near it. Edit to add: is not that security bugs are not important, but is not what we (embedded developers) are always chasing or taking away our sleep at night. Embedded is wide enough beyond computer networks.
Please take a look at the room around you. Can you count every little chip in there? Including the one in your keyboard, LED lamps, coffee machine? Are all these ARM? NO. Are all these connected to the network? NO. Do you expect them to work 100% of the time without hiccups? YES, and they probably do without you noticing it.
And they are probably running 1000s of lines of C. That's embedded.
And you don't think having the compiler check memory usage would be useful in this case? In my mind it means more effort can be put on making sure the logic itself is correct.
Would the compiler check a write outside the buffer that is filled with sensor data? Is that it? I'm not allocating anything so I cannot double free. The program executes in a main loop so there is no data races (perhaps one if the data buffer is shared with an interrupt, but it's a problem as old as the bible and we already have 1000 libraries with safe circular buffers).
Or is the check done at runtime? Will it waste my cycles?
Is that all I should change my language for?
Would the sensor manufacturer help me if I give them a failing case written in Rust?
Is it worth the risk of compiling on a nightly thing that will change next week? Note that people might get hurt for real.
Damn! Now my MCU/sensor is going NRND. Should I freak out because Rust is still not supported for that new MCU yet, and I should write the thing all over again?
I'm getting really pissed off about people downvoting who have nothing constructive to add to the conversation.
Rather than downvoting, fucking REPLY. A downvote is lazy--it's actually worse than doing nothing on a low-vote thread--it's a sin on the level of turning down the premise when doing improv comedy. An articulated position furthers the conversation and actually represents effort.
When did HN readership become such prissy hothouse flowers? FFS.
(EDIT: I turned down the swearing a bit)
I suspect Rust will be a clear win on some subset of projects, and will not be worth it others.
Industrial is a very poor choice of counterexample. You should have stayed with consumer space counterexamples. Everybody in the industrial setting wants their telemetry and data transmitted to centralized computing whether they know what they are going to do with it or not.
As for sensors, how are those distance sensors connected to the microcontoller? Perhaps a redundant automotive bus with real-time constraints like CAN? CAN often has a fairly decently sized, badly written, vendor supplied communication stack in order to use it.
Is that forklift's sensor telemetry going anywhere? If not, then you're probably going to lose out to someone who does when you sell that forklift to Amazon or WalMart. They probably want to know about where the skulls are (gotta track those lazy humans) more than they want to know about where the forklifts are.
> Are all these ARM? NO.
Actually, they increasingly are. USB-C PD negotiation chips, for example, are basically ARM M3/4 cores with specialized hardware. The cost differential between 8-bit and 32-bit microcontrollers is so low that unless you have extreme volumes, you might as well use the 32-bit one and gain all the infrastructure that provides.
> Are all these connected to the network? NO.
Are you sure? Hotel locks had security vulnerabilities because they had a USB port.
The problem isn't whether I can use the I2C bus on your Cuisinart to break into it--if you used a "standard" driver written in C I probably can. These small system don't have to worry about "secure" simply because they aren't worth breaking into--until some really creative person makes them worth breaking into. (Hey, there's a speaker on that innocuous thing that's loud enough that we can talk to Alexa. YAAAARRRR, Me Mateys!)
Regarding ARM, it depends. It's not just a matter of bits over price. Pretty often the combination of features you want are not available in an ARM chip. For example, long term support, some specialized tool, temperature/power supply range, price, available peripherals, etc. And sometimes you might be required to work on some strange 32-bit CISC from Renesas just because the salesperson took the purchasing manager to dinner somewhere.
And yes, everything is hackable no matter the language you use. If you open the case and cut some PCB traces you might read and inject some data making some device to misbehave. But that's the way it is and I don't see the world falling apart because of this. If it really matters, then (electronically) tamper-proof your case would be my advice.
Again, is my honest opinion and the opinion of many embedded developers I work and know: vulnerability FUD is just noise. I'm just being sincere. Of course if you make locks you have to make your system secure. The other billion of embedded applications might not. And that's it.
No argument. But this is becoming less and less true since ARM has even become standard in automotive.
> And sometimes you might be required to work on some strange 32-bit CISC from Renesas just because the salesperson took the purchasing manager to dinner somewhere.
HAH! Been there ... screamed about that.
We're probably not as far apart as you think, if I'm just writing 100 lines, I'll just reach for C and be finished in an hour or two.
My problem is that none of my projects ever stop at 100 lines. They evolve and only get bigger. Eventually they adopt a "stack" (CAN, BLE, TCP/IP, etc.) from some manufacturer, and it's all downhill from there.
I need help to manage that explosion of complexity and the torrent of bugs it creates, and C just doesn't provide it. I don't think I've programmed an embedded device smaller than the original PDP that K&R developed C on in probably 10 years--maybe more.
And this includes FDA cleared products. The days of just sitting on an unchanging product for 15 years after getting clearance are gone.
Let me give a very simple example in golang. Function returning single value and function returing multiple values.
func foo(x int, y int) int {}
func bar(x int, y int) (int, int) {}
Even though below synatx works,
func bar(x int, y int)(int) {}
why there is a additional way to do it by removing parenthesis ?
This looks very simple example. But it breaks consistancy of syntax. Adds 2 rules to remember over what saving 2 keystrokes ? In the age of 100x full stack devs, life really sucks.
* First, bit field descriptors is a huge pain - where, in C, you would define an enum for the task, there isn't a real equal in vanilla Rust, other than a litany of constant variables. * Second, the over-reliance on nightly is a tough sell for - having inline asm gated makes it very hard. * Third, while format is great, it's also very heavyweight. Not having (AFAIK) variadic arguments is hard, and there isn't really an equal to printf.
I would love to be proven wrong, because I have an OS to write, and would love to use Rust.
“A litany of constants” is exactly what your c enum is though.
And you can define a "degenerate" Rust enum with explicit discriminant values:
enum Foo {
A = 1,
B = 2,
C = 4
}
They are cast-able to integrals too: println!("{}", Foo::C as usize); // prints "4"
Doesn’t work so well for flags however.> Second, the over-reliance on nightly is a tough sell for - having inline asm gated makes it very hard.
The alternative would be to simply not make the feature available at all until it is done and ready, with even higher risk (as it would have been exercised even less).
As is, if the feature is fine for you as-is you can use it, and if you can't or don't want to use nightly you can go and assist its shepherding to stable (https://github.com/rust-lang/rust/issues/72016)
> while format is great, it's also very heavyweight. Not having (AFAIK) variadic arguments is hard, and there isn't really an equal to printf.
There are no safe Rust-level variadics, but Rust can call C variadic functions, and under RFC 2137 (not stable yet I think) define them as well.
enum Field {
Foo = 0b11111111,
Bar = 0b10101010,
}
fn main() {
println!("{}", Field::Foo as u8);
println!("{}", Field::Bar as u8);
}
There is also the bitflags crate: https://docs.rs/bitflags/1.2.1/bitflags/For lighter weight formatting there is https://github.com/japaric/ufmt
Is this (C-style enums) new?
I swear I looked for it a year or two back, found questions about it, with the answers "use constants".
C-style enums are sugar for defining an integer type alias and a bunch of constants without creating any new types or scope.
Rust enums, even with an assigned value, define a new type that can only be one of the defined values. So this type is not an integer and cannot have a bit pattern that isn't explicitly defined.
("style" is supposed to reflect that it's similar, but not the same thing.)
I already have enough trouble trying to explain to new people the difference between C and Rust enums. The phrase "C style" just confuses things more, IMHO.
Admittedly this may be a failure of communication on my part.
I don't think the name was super carefully considered, to be honest, it just kinda happened.
enum Foo {
A=0,
B=1,
C=3,
_UNKNOWN(i32),
}
However, this cannot be compiled by current Rust compiler.They're referenced as a pre-existing feature in this Jan 2015 RFC [0], so they predate Rust 1.0. I don't know quite how old they are, but I suspect much older than that as this feature is necessary for interfacing with C which has been a high priority for Rust for a long time (particularly driven by the needs of the Servo project in the early years).
[0]: https://rust-lang.github.io/rfcs/0639-discriminant-intrinsic...
No.
> I swear I looked for it a year or two back, found questions about it, with the answers "use constants".
Possibly because they were asked / answered in the context of bit flags?
Even degenerate, rust enums are not compatible with C enums because they're still type-safe, you can't create any random value and say "that's a Foo", that'd be UB. So if you want bitflags, you need to use constants.
Another struggle I remember was just getting the max defined value. Again the Rust type is pretty Opaque because it hides the all-powerful discriminated union type. I love the Rust enum type, but I wish Rust also had a first-class dumb enum type, with all the C/C# niceties (bitflags, runtime enumeration, selection of backing field). This seems like quite a different from what the rust enum is.
The other stuff isn't built in because it would add a bunch of code you may or may not want to your build, and it's easy enough to opt into. We also could solve that problem, in theory, a few ways, but there just hasn't been a ton of demand for it, so it hasn't been done.
With bitfields, there has been increasing demand lately, so we'll see.
I'm talking about code that ends up looking like this: https://github.com/retep998/winapi-rs/blob/0.3/src/shared/nt...
You get type safety because you can define the type of constant rather than use some primitive, but there feels like something is something missing (nicer autocompletion, better auto generated documentation, and so on).
Have a look at the bitlags crate, I use it a lot for this purpose: https://docs.rs/bitflags/1.2.1/bitflags/
You still have to give the literal value of the field instead of using the auto-incrementing enum but that's how I do it in C as well, I find it too error-prone otherwise. And how do you deal with fields that take up multiple bits anyway?
>Second, the over-reliance on nightly is a tough sell for - having inline asm gated makes it very hard.
I agree 100%. I can understand them prioritizing higher level system programing and taking the time to do asm right, but it does make it a bit of a pain to do bare metal Rust at the moment.
>Third, while format is great, it's also very heavyweight. Not having (AFAIK) variadic arguments is hard, and there isn't really an equal to printf.
What do you have in mind here? format! is strongly typed and a lot more flexible than printf. You can't even print custom structs with printf... Actually even printing standard types like uint32_t and friends require macro soup to work portably.
Variadic arguments are pretty much a no-go for a strongly typed, static language since you would need a lot of trickery to get it to work safely under the hood (hence format! being a macro).
But what use case do you have for printf that can't be implemented with println!/format! and friends?
I'm guessing they mean that the format! machinery is rather complex and brings in a lot of code. That it's also somewhat slow is a recurring concern .
Have none of the embedded folks created a specialised version of format! which is not generic, and basically only supports the types and styles which c's printf supports?
edit: there's ufmt though it seems somewhat inactive and I've no idea how good it is: https://github.com/japaric/ufmt
> Optimized for binary size and speed (rather than for compilation time)
> No dynamic dispatch in generated code
That sounds awesome. Personal project or commissioned?
https://defmt.ferrous-systems.com/
(And consider sponsoring them if this is of value to you: https://ferrous-systems.com/blog/knurling-one-month/.)
With my Rust language team hat on: we'd love to have native support for bitfields. I'd invite folks who are interested in that to propose a language MCP ("major change proposal"), which I'd expect to be approved, and then collaborate on a spec for them.
1) Having uN and possibly iN types, for general-purpose use.
2) Placing such types at specific offsets within a struct.
C only handles the second of those: you can have an N-bit type, but when you extract it it comes out as the wider type you constrain it from. So, `unsigned field:5;` will get read as an `unsigned`, not a `u5`.
Rust could handle both, if appropriate sized types exist. (Const generics might be nice here, to parameterize on the type width.) (1) alone won't solve the problem, without some way to define how those types are packed into the struct.
How do you define quick? Python has a large standard library and so you don't have to hunt for functionality on crates.io and then download/compile -- I have written scripts faster than that.
Writing scripts with Rust likely will be slower than Python, but I don't really buy that having functionality in crates is part of that.
Except that, if its in the standard library, you don't have to choose between multiple implementations. I have written scripts faster than I have chosen dependencies.
Rust is a great C++ alternative. But seriously, the community has cult like marketing.
What in your opinion makes one language cult-like vs. another that many people like and recommend to others?
Cults generally have a very negative impact on those who are members vs. the leaders. Usually the members of cults are taken advantage of and are only there to support the people at the top.
The Rust community is very far from anything cult-like.
The documentation is great. Various channels to interact with the community have always been polite and helpful.
Heck, one would think that the author of a leading book on the language popping up everywhere to give helpful answers/clarifications would be seen as welcome and not in-your-face-activism.
The original poster was saying that generally cults will be rather unpleasant for most mid<->lower level members, but are pleasant for the people at the top (generally by extracting value from those lower-level members, whether it's labor, money, or sexual favors).
This seems a bit counterintuitive, of course - "if it's unpleasant, why the hell wouldn't they leave?". Usually cults have coercive tools that either make it difficult for someone to leave, or manage to brainwash them into believing they would be more unhappy outside of the cult.
[1] https://dictionary.cambridge.org/dictionary/english/anything...
Edit: acomar points out that the "haven't" negates what follows, so interpret what I wrote above as "the phrase on its own means not X".
I'm appalled at myself for the silly manner in which I phrased that. I guess I intended to say: "I have found it nothing but pleasant", but instead ended up tying myself up in knots.
For what Rust offers, it is unreasonably effective as a quick scripting language, with one big caveat: Once you’re accustomed to the language and have significant experience with it.
I’ve built and own CLI tools in Rust, used in production, where the alternative language choice was python, and the experience has been way better than I imagined. I expected to trade a ton of developer productivity for the extra speed and safety, but the reality is that it’s not even an integer multiple. Not even close. Again, my expectations were different, but in reality it’s been really pleasant.
Not including integration testing, the whole deal was two days of work. If I had to pull in and evaluate external libraries I would have spent two days just working out the details of which libraries to select.
At least for now, I don't think anything can replace Python for prototyping an idea.
I generally agree, but it has some weird holes. There isn't an OrderedSet, it doesn't have a binary tree like Java's TreeMap, its HTTP client isn't great (people mostly use Requests), and there's a lot still missing from its asyncio library.
> web UI for managing email sequencing
You built a web UI without even using Flask?!
I remember telling my uni professor about that 15 years ago, but she swore by using C for that purpose. It's what you know I suppose, but if you know both languages, Python is much better suited to the task.
This is not an objective opinion, but subjective. People generally appreciate different things about different languages, and it’s entirely possible for some who knows both languages to disagree with you here.
For quick CLI tools, my favorite thing about Rust is that it has really high quality command line parsers available in the community, and they are type safe.
I’m not suggesting that your opinion is wrong, only that the way you phrased it implies that many people could come to the same objective opinion, while there’s plenty of evidence that folks pick languages they enjoy and prioritize different things than you might.
That's just not how reality works, there is usually an objective truth even when we can't agree on what it is. You can use a hammer and a screwdriver for putting screws into drywall, but one tool is objectively better, under most conditions, regardless of your personal opinion.
Python is superior to C and Rust for most kinds of scripting tasks, given equal mastery of both. It has a REPL, it doesn't have a compile time, it has a great batteries included standard library. There are plenty of specific tasks that will be exceptions, but that's my subjective opinion. I also think it's closer to the objective truth - and that people who have a different opinion are simply wrong. I'm not afraid of calling it how I see it. I could be wrong here, but I don't think that's the smart money bet.
That Python and that Rust are not very far off from each other. Rust is surprisingly good at these tasks for people who already know Rust. It's not as far out as you seem to think.
That being said, I am quite sure that Rust is not going to take over as a scripting language or something. But at the same time, it is not completely ridiculous to write some Rust for these sorts of tasks if you're already comfy, and if the libraries are there.
I don't agree that it's better than Python for most short scripting tasks - but I will grant your point that it's not completely ridiculous either, not by a long shot.
I still think I'd rank it behind Go and Python for a typical website backend - but it depends on the project and the team.
The more people involved, the more things the type system will save you from, and the more you benefit from being able to build powerful abstractions - which offsets the relatively higher "tax" on productivity with it.
Rust is one of the better choices for backend web development today, a sentence I also find surprising just to write.
Honestly, probably the biggest thing holding it back for that use case is not the language itself but lack of adoption. It's tougher to hire for, and has a less mature ecosystem. I feel the momentum is there and that will change over the next 5 years or so.
Some Oxide folks have talked about our experiences with Rust as web backend stuff, it's linked in the post but https://blog.jessfraz.com/post/the-automated-cio/ and https://twitter.com/ahl/status/1308421852931461126 We had to build a bunch of stuff because as you say, the ecosystem isn't super mature at times.
This is a poor analogy. Programming languages are used for writing programs. You suggest that Python is objectively superior in one context "scripting" than other programming languages. There are languages that are not cross-platform, that would fit that, there are languages like PL/SQL that are limited to databases, those are not practical for many program development efforts. In this context, we're really talking about two different types of hammers, perhaps one has a claw and the other does not, but they are both fairly portable and can target nearly all the same platforms.
But if Python is objectively better than these other general purpose languages, does that imply that there are areas where Python is not superior to these languages? Would it suggest that folks who've been successful even when choosing Python in those cases objectively made the wrong choice?
It's an analogy, but one tool is going to be faster and easier to use. So let's say a screwdriver vs an electric screwdriver.
> But if Python is objectively better than these other general purpose languages, does that imply that there are areas where Python is not superior to these languages?
Yes. There are a lot of areas where Python is inferior. In fact I'm using Rust right now for a project despite having over 15 years experience with Python. Programming languages are tools and they're better or worse suited to different tasks. There is no one language to rule them all - yet anyway.
> Would it suggest that folks who've been successful even when choosing Python in those cases objectively made the wrong choice?
Yes. That you were successful does not fully validate your decision about choice of programming language. It just means it wasn't that bad. It does not mean it was the optimal choice.
I wonder how much this has to do with you finding Python to be better suited to quick scripting tasks.
Even for small scripts, I find Python to be fairly clunky to use, and just having simple type checking would speed things up for me considerably.
Trying to replicate bioinformatics work is a huge PITA because of this type of approach.
I have had small 'scripting' tasks that are successful accrue enough feature creep over time and turn into a 10000 line behemoth (as far as quick and dirty scripts go). And at that point, you start hoping you hadn't slapped it together with the haste that you did and miss the easy refactorability that Rust gives you.
That is not a reason to start off thinking it will get that big, so you have to over engineer it, because the vast majority of scripts don't. You design to the most likely scenario.
Every single time my “quick” Python script has turned into something I use for more than a week I say “damn I wish I had written this in Rust”.
I think it is just your private /subjective opinion and it is not any better than mine.
Or maybe they assume that, if you have that, why wouldn't you make the jump to writing a REPL, since the hard part is done...
Just making some guesses.
"Python is superior to C and Rust for most kinds of scripting tasks, given equal mastery of both. It has a REPL, it doesn't have a compile time"
To me it makes the impression that you are conflating here, hence my answer about REPL. Technically of course REPL is different from having "no compile time".
In any case I do not really consider opinion about Python being the better one for scripting tasks as anything but subjective.
It however does not have Python's batteries-included standard library. Luckily Opam is starting to make that less of an issue.
Given a specific task and 100 programmers equally skilled in both languages, they will accomplish the task faster in one. There will be a clear and statistically significant answer if one were to do a controlled study. It could be that the result is that they are nearly equal, but that would be a surprising outcome.
So when you say my opinion is the equal of yours, you're just saying you have no idea which is closer to the objective truth so you can't compare them. But fundamentally they are not equal and one opinion will be right and one will be wrong (in this case because it's a boolean outcome).
It is my opinion, it is subjective, but I also am willing to bet it's right. And there is a right and wrong answer here regardless of our ability to know it for sure.
One thing that sticks out in a scripting mindset is the various string types, but I mostly accept that as a small price to pay for doing things correctly (OS strings vs. UTF-8 vs. C strings).
Because of the kinds of things I build with Rust, I've had to write quite a few cargo build scripts (build.rs files), which are very bash/Python script-y in nature. Not once have I wished for a different language!
Here's a very simple one: https://github.com/alexcrichton/brotli2-rs/blob/master/brotl...
Here's one with much more complicated requirements: https://github.com/sfackler/rust-openssl/blob/master/openssl...
- low-level programmers looking for safer tools to wrangle big, beastly projects.
- high-level programmers looking over the fence because this is the first low-level language that has a bunch of quality-of-life features that are "table ante" for them considering working in a language (most of them only tangentially related to safety, although the safety's probably a huge leg up for them). One of these features of course is the language getting mainstream and survivable enough that they know there will be a long-running library ecosystem.
Thanks Steve!
We have a Windows working group that does far more work than I do; honestly, I mostly am just a visible person who uses Windows, more than an expert or someone who fixes a lot of big problems. I do file bugs and patch things where I can.
I like the u32::from_le_bytes and into() and so on, but I feel like Rust would be a perfect match for explicit "u3" types when dealing with low-level protocols.
https://immunant.com/blog/2020/01/bitfields/ and similar articles show some options, but none of them feel as natural (ergonomics wise) or even as "safe" as a C bitfield checked by clang.
bcantrill (or others): am I just missing something here?
I only return to Rust about once or twice a year (currently) and keep cutting myself on an ergonomics thing like this. But I’ve also seen it improve year over year at a remarkable pace. So I’m hopeful, but also always wondering “huh, am I holding the phone wrong?”.
We use https://crates.io/crates/bitfield, and some of our dependencies use https://crates.io/crates/bitflags. They're not perfect, but they get the job done.
The nginx example in the above blog post seems like a bitfield! equivalent would be... unpleasant.
For very small structs that are just only wire formats, I might use bitfield!. But encoding/decoding protocol headers (e.g., a VP8 header [1], which has a 19-bit “size” field) or interop with other systems where you’d like some checking a la serde, bitfield! seems more verbose than “fine, I’ll do it myself right here against the u8”.
That is, even if that nginx example had a two-bit field in there, I’d take everything at the end and round up to a u8. Then read/write some of these “by hand”, rather than pollute the struct with lots and lots of macro “noise”.
I was curious to see what you chose to do for representing arbitrary bit width. I didn't see an example / test for something like u19 though.
Your backend compilation seems to generate calls to an arbitrary width [1], but I only see explicit 8/16/32/64s generated or tested [2]. The code definitely seems ready for size != width, and answering some of my question "sizes less than 8 round up to u8, otherwise round up to nearest power of 2" [3].
Either way, it looks like a great start! Your little language seems like it probably has enough in it already for my VP8 example.
[1] https://github.com/mwylde/protogen/blob/f812dd74e5cebe680422...
[2] https://github.com/mwylde/protogen/blob/f812dd74e5cebe680422...
[3] https://github.com/mwylde/protogen/blob/f812dd74e5cebe680422...
There are details that matter, but on the codebase I work on, the current worst case (before the work I've been doing) means a compile takes a couple of minutes; more like two than five. I'm not sure how other codebases at the company are with regards to this, but I don't think they're super long.
What I've been doing drops it to under thirty seconds in basically all cases.
So yeah, it hasn't been a super pain for us specifically. We'll see as time goes on :)
I will say that I recently discovered that putting a bunch of crates into a workspace is inherently slower than compiling them all separately, which is pretty surprising. I haven't dug into the exact reason yet, but it makes 4x the syscalls, so I'm assuming that it's checking every single dep of every single member and that has significant overlap.
https://users.rust-lang.org/t/complex-build-advice/48779?u=s... is probably closest to what I can currently say :)
> The issue with Makefiles is that well, they're not portable. I'm on Windows, and 99% of this stuff Just Works really really nicely, but that means that "just use Make" isn't a real option. I love that Rust's tooling is so cross-platform, and this is just a final pain point. The problem is hard, but it's mostly about fiddly details.
> I'm trying to figure out how good I can make this without throwing it all out and doing something else. If we never push Cargo's boundaries, it will never grow into being a good fit for these projects.
The fundamental problem with Cargo is that it's not a general-purpose build system (like, say, make). It's a black box with a bunch of hooks for achieving certain common things that the black box itself doesn't handle. As a result, every time you step out of the "supported area", it will be yet another "final pain point" & "mostly about fiddly details".
I agree make is not a good fit for a cross-platform project (nor is Meson, IMO, due to the Python dependency). But there are good (IMO) options in this area that meet your requirements (cross-platform, no external dependencies, etc). I can elaborate if you are interested.
The tool I have in mind is buid2[1] (full disclosure: I work on the project). In this context you would be using it as a portable (and saner) make replacement (though we do have a Rust module in the works but that is geared more toward multi-language C/C++ and Rust builds).
In fact, if you are interested, I am prepared to put my money where my mouth is and code up a demo/prototype for you (without any expectations that the result will be used).
[1] boris@build2.org, berium on reddit, boris-kolpackov on internals.rust-lang.
Is that for full builds or incremental? 30 seconds for a full build is reasonably fast (for rust, still feels slow IMO) but if that's incremental it sounds _painful_.
tl;dr if you have a lot of dependencies more cores help a lot. If not, you hit diminishing returns; top hardware _helps_ but it's still painful.
Does that happen for some couples?
Makes one long for the days when ads were shown in pop-ups and you could at least close them. And the ad people did warn us this would happen :-)
From a foreigner's perspective, Citizen Kane is a (very) good movie but really nothing special. I enjoyed Rashomon a lot more. However, Rashomon was clearly inspired by it. To really understand the appeal and significance of CK, you have to compare it to movies before it. The reason CK doesn't amaze many people anymore is because everyone now uses these cinematographic techniques! In similar vein, the first Half Life game was groundbreaking and pioneered new ways of story telling. But if you don't remember earlier games, you may be scratching your head.
Every now and then a programming language comes out that is a gamechanger. Such a language won't get out of fashion unless new programming languages start building upon it. I can't say if Rust is one of them.
I don't have a long career in programming, and I know only a few languages, but I'd say Python is one of the languages that managed to set the bar for others. It's still going strong, but it's no longer turning heads with its usability and clear syntax.
Personally, I would expect that the string
"the quick brown fox
jumps over the lazy dog"
Would be like this in runtime: "the quick brown fox jumps over the lazy dog"
Whereas this: "the quick brown fox\n
jumps over the lazy dog"
would be like this in runtime: "the quick brown fox
jumps over the lazy dog" let encoded = this.textContent.replaceAll("\n", "\\n")
.replaceAll('"', '\\"')
return `"${encoded}"`
[1] http://sergeykish.com/c-multiline-stringIf you try to exclude newlines specifically, it becomes messy when you have to deal with indentation. E.g. how should this parse?
foo = "this is a
multiline
string";
If you simply ignore the newlines, you'll end up with a bunch of spaces. If you start collapsing whitespace, detecting indentation levels etc, parsing becomes a lot more complicated, and harder to explain (which you still need to do, because it won't always "just work").The same hidden newlines though in a string will alter the string: they will make the string multiline.
So, in order to be more consistent with how code is formatted, I'd prefer hidden newlines in strings to not modify the strings.
I’m dying to find a tech that would let me easily write a shared lib that targets web , android and ios, but so far everything seems very experimental wherever i look. Rust seemed to be the closest, but still a bit raw.
I believe Cloudflare is doing this with the 1.1.1.1 app; maybe someone who is there will see this and chime in.
omg!
In the formatting sublanguage, braces denote a value to be formatted: `format!("{foo}", foo=42)`
Instead of a name you can identify values positionally, counting from zero: `format!("{0}", 42)`
You can omit the identifier entirely and the arguments will likewise be interpreted as positional: `format!("{}", 42)`
To tell the formatter the manner in which you would like to format the value, you can give format specifiers. These are separated from the value's identifier by a colon. An example of a format specifier is `b`, which prints the value in binary: `format!("{foo:b}", foo=42)`
As before, you can leave out the name to refer to the value positionally: `format!("{:b}", 42)`
Other format specifiers are `?`, which tells the formatter to print the "debug" output (i.e. autogenerated output useful for debugging by programmers), `#` which tells the formatter to insert line breaks and indentation for easier reading, and `x` which tells the formatter to print numbers as hex.
Nit: not necessarily autogenerated, we can implement Debug by hand. It's just that, as the name indicates, this is aimed at debugging output rather than the possibly-user-facing output of Display.
I guess so, but I'd kind of prefer to be able to do everything I want in safe mode.
Different people have different needs and want to do different things. It is impossible to satisfy everyone's needs – "do everything I want in safe mode" – both from a practical perspective (the language and the compiler would become incredibly complex and therefore buggy) as well as from a theoretical perspective (you would solve the halting problem, among other impossible feats).
Ats[1] isn't in such bad shape. Its type system is much more complex, sure; idiomatic ats proves many interesting programming attributes that rust doesn't. But if you write ats in the same style as rust, the only additional complexity you'll notice is you have to manually drop.
> you would solve the halting problem, among other impossible feats
...what?
I don't see what the halting problem has to do it. Safe rust programs aren't required to halt; nontermination is safe, even if not necessarily desirable (like memory leakage, which is also allowed for the same reason).
Is the following code safe?
function_xyz();
let p = 0x1234 as *mut u32;
p.write(5);
It is safe iff function_xyz() does not terminate. To determine whether the code is safe in the general case, the Rust compiler would have to solve the halting problem.Since that is impossible, the compiler uses a heuristic, which defaults to "not safe". It could use a better heuristic, like "Does function_xyz() contain an infinite loop? If yes, the code is safe." But that's of dubious value.
It doesn't matter whether it's useful or not. What matters is that the compiler can't determine whether it's safe or not.
In languages like C# or Java the whole standard library is written in C# or Java. Somewhere deep inside implementation of new() there's a function call to C malloc() or some equivalent. The important difference, native API surface is much smaller this way. All else being equal, an API with a small surface has better chances to be secure than a large API.
C# has an unsafe feature quite comparable to Rust. Java has its sun.misc.Unsafe module. These approaches are not meaningfully better than what Rust does.
The standard library, and 95% of third-party libraries, don’t use these features.
Rust standard library contains a huge amount of unsafe code, with a history of security bugs there: https://medium.com/@shnatsel/how-rusts-standard-library-was-...
Compare to Java or C#. Vast majority of stuff, including these collections, are implemented on top of memory-safe primitives. Usually (unless explicitly writing unsafe code which most people don’t do), the only attack surface is the VM itself.
The attack surface is much smaller in comparison, both JVM and CLR are very simple from the outside, 202 and 234 instructions, respectively.
The attack surface is well-contained, third-party C# or Java libraries don’t patch the VMs, while unsafe rust is used in many third-party crates not just in the standard library.
Also, these VMs are well-audited (most of the implementations are open source now) and widely used all over the industry, including very security sensitive applications like banks and military.
I enjoy the content of his talks very much, but the constant shouting is very tiring.
We've talked about having support for user-defined literals, so that you could have decimal literals; in the meantime, a macro works for that.
Fixed point is where say in a 32 bit word, 16 bits is used for the integer part and 16 bits for the fraction or any combination that you can declare. Usually notated like 16.16 or 16:16. Another format example in 32 bits could be 8.24 and also formats in other word sizes.
I found some crates using 'fixed' as the search term.
https://crates.io/search?page=1&per_page=10&q=fixed%20point
It's been my desire to have a language for embedded to have first class support for fixed point just from the ability to work with them like you would a floating point number and not have to worry about macros.
Along the same lines, is there anything that'd be needed to have "first class support" other than literals? If we had user-defined literal formats, so that one of those fixed-point crates could define a format for fixed-point literals, would that suffice?
Multiplying a Q8.24 with another Q8.24 makes a Q16.48.
In C that's not the same as the '∗' operator; you'd generally cast both arguments to 64-bit first, and rely on the compiler being smart enough to use a 32x32->64 instruction if available instead of 64x64->64. But even that's ambiguous - is it signed or unsigned widening multiplication?
Adding a Q8.8 to a Q4.12 needs a 4-bit shift before the addition. So does adding a Q5.27 to Q1.31.
The right thing to do is, unfortunately, ambiguous.
E.g. when adding Q5.27 to Q1.31, do you need to keep the full range and precision and end up with Q6.31? Or do you right shift the second argument, ignore overflow, and call it a day with a Q5.27 result that is implemented in C as (uint32_t)(a + (b >> 4)? (Though if it's signed, which it usually is, (b >> 4) is dodgy in C anyway as it's implementation-defined.) If you do ignore overflow, is that a saturating addition (like used for audio/graphics) or wraparound (like in C)?
Really, those decisions depend on what the numbers mean, as well as range and precision assumptions the author may already know are valid for numerical reasons. So maybe there's a case for traits on number types which specify whether they are default-saturating, default-precision-maintaining, default-range-maintaining.
Another operation is to change the representation, either to truncate/round some precision bits, reduce range bits (saturating or assuming), or expand range bits prior to doing some sums (because addition doesn't expand the range bits in real implementations, you have to explicitly do that yourself in advance of adding).
The way I see this done in careful DSP in C or C-like languages is as a series of function or macro calls that specify input and output formats, avoiding operators and the ambiguity they imply.
I know there are some TI and ADI arch's with special fixed point data types built into their compilers (which are nonstandard extensions to C/C++, fwiw), but they are unsupported by LLVM last I checked.
I've also heard that those niches are having their lunch eaten by ARM M4/M7 cores which have floating point vector instructions, and you can write Rust to target those today.
Why can't we extend source transformation? The feature is useful - lisp known for macros, javascript recently got string transformation:
b = s => parseInt(s, 2)
b`101010`
42
Of course this requires changes to the language. Can we solve it with markup [1]?They are part of C++14 standard:
Nothing beats PC / Amiga inline Assembly code blocks / compiler intrisics.
The frontend compiler is intelligent enough to understand Assembly, instead of blindly giving it to the backend, like it always happened on UNIX compilers with their separate phases as processes connected via pipes.
Nowadays they have intrisics which are a mix of specific CPU ones, and others that are general purpose ones.
Then the macro Assemblers that come with them are powerful enough to program almost C like code in Assembly.
Check VC++ inline Assembly, intrisics and MASM.
Borland had similar offerings with Pascal, C, C++ and TASM.
Other PC and also on Amiga, had similar offerings.
Delphi/Free Pascal, VC++ inline Assembly and intrisics.
https://www.freepascal.org/docs-html/prog/progch3.html
https://docs.microsoft.com/en-us/cpp/assembler/inline/asm?vi...
https://docs.microsoft.com/en-us/cpp/intrinsics/intrinsics-a...
With the Microsoft syntax you were generally stuck with having to fix specific registers, and hope the surrounding code was ok working around the asm code and its unstated and excessive constraints.
Whereas with the GCC extended assembly syntax, you can tell the compiler that it has a choice of which registers to use, which memory locations are read from and written to, and many other options that the compiler is free to optimise with.
The GCC version is much better for optimisation. It can even optimise away assembly instructions whose output is stored in a variable that happens to be unused by another part of the program, and select between code using registers or code using inline constants or direct memory operands, depending on expressions that are passed to functions. It also works with "new" instructions that the compiler doesn't know about but the assembler does, or even assembler macros and linker directives that the compiler doesn't know about. The Linux kernel has used all this flexibility to great effect since the beginning.
But I'd be happy to discover something new.
Care to show me a real-world, working example of compiler-optimisable assembly inlines with something that isn't using the GCC extension?
If by DMD you mean the Digital Mars D compiler (which is called DMD), then:
No. I checked the online documentation just now for DMD x86 just to be sure. According to that documentation, its inline assembly doesn't provide any of the flexibility that I listed.
You can see that easily by the way DMD requires you to name the exact registers in the inline asm, and the way the operands syntax is fixed as either a register, memory, memory with base and offset etc (the usual Intel things).
But DMD doesn't let you say "pick any suitable operand type". With GCC the compiler will select among possible operand types according to what fits best with the surrounding code. This way it avoids unnecessary loads, stores, register transfers etc. when interleaving C with asm, intrinsics with asm, or even asm with other asm from different inline functions.
You certainly can't do any of the fancy stuff the Linux kernel relies on, with asm directives like '.pushsection/.popsection', memory fault fixup tables, asm goto, inline lock hot paths and cold paths, etc.
As for the rest, I am more than happy with VC++ inline Assembly and compiler intrinsics (which then VC++ can play register games if that is what you're after), as another example.
The widespread of GCC hieroglyphics inside strings, is an unfortunate state of affairs in what concerns inline Assembly.
But the techniques I listed, which Linux kernel (and other software) relies on for performance, can't be done in C, can't be done with compiler intrinsics, and can't be done with basic asm in the style of DMD and MSVC either.
A feature to add new program-specific intrinsics to the compiler would work, but I don't know of any compiler which offers that, and it would probably look like extended asm anyway.
The GCC syntax isn't pretty. But I can't agree that it's inferior unless someone points to a better alternative that achieves similar results.
I've written programs that were portable to both GCC and MSVC, using the inline asm syntax of both compilers. The MSVC version produced slower code, because there was no way to produce the asm I wanted using MSVC.
Well it turns out I can't, and neither can anyone else if they are looking for the fastest performance, because the fastest assembly depends on the context it is called from.
I had this problem going all the way back to the 8-bit machines, which is why I took an interest in compilers and dynamic code generation. To get the best :-)
These are highly architecture-specific. If you care about this, you can implement more idiomatic asm support in a separate crate, that parses some sort of custom syntax and then uses the default asm! feature for implementation.
We haven't considered it, really, and that's mostly just because it well, feels wrong. We're just pinned to a specific nightly for now. It's no biggie.
This is also just for one repo, I should say too; we use stable for everything else. Operating systems stuff on stable Rust will be a glorious day...
I'd like to know what Oxide is up to...
Also I can highly recommend their podcast, I can't wait for the next season.
I saw the talks, but I still have zero idea of what it is they want to sell.
Here's a thread from last time this came up: https://news.ycombinator.com/item?id=23979042
Like you, I'm from the generation that grew up treating "computer" as synonymous with "personal computer", i.e. something on a desk or in one's lap. For some reason I can't quite articulate, it saddens me that a new company with "computer" in the name is focusing on those other things called computers in far-away buildings that we can't access. I hope that once Oxide starts making money from servers, it can branch out into workstations. Then, to me at least, it will more fully live up to the name "Oxide Computer". Yes, I know this association is more emotional than logical, but there it is.
In C++ that’s part of C++/14 standard, well-specified.
> we have u8, u16, u32, u64, etc
It’s indeed nice to have fundamental types like that, but <stdint.h> header is required by the standard since C99, in C++ since C++/11. It provides typedefs like uint32_t and the rest of them, thus the same UX.
> DWARF support
Yeah, but no PDB. Ship a Windows program written in Rust, and when it crashes or hangs on end-user’s PC you’re pretty much screwed. You need WinDBG to analyze these memory dumps written by the OS, and for that you need pdb symbols as opposed to dwarf.
Can you expand on this?
C:\rust>cargo new demo
Created binary (application) `demo` package
C:\rust>cd demo
C:\rust\demo>cargo build
Compiling demo v0.1.0 (C:\rust\demo)
Finished dev [unoptimized + debuginfo] target(s) in 2.28s
C:\rust\demo>dir target\debug
Volume in drive C is Windows 10
Volume Serial Number is B4A6-FEC6
Directory of C:\rust\demo\target\debug
10/11/2020 02:03 PM <DIR> .
10/11/2020 02:03 PM <DIR> ..
10/11/2020 02:03 PM 0 .cargo-lock
10/11/2020 02:03 PM <DIR> .fingerprint
10/11/2020 02:03 PM <DIR> build
10/11/2020 02:03 PM 61 demo.d
10/11/2020 02:03 PM 152,064 demo.exe
10/11/2020 02:03 PM 1,101,824 demo.pdb
10/11/2020 02:03 PM <DIR> deps
10/11/2020 02:03 PM <DIR> examples
10/11/2020 02:03 PM <DIR> incremental
4 File(s) 1,253,949 bytes
7 Dir(s) 15,995,387,904 bytes free
`demo.pdb` is readily available.C99 requires stdint.h, but does not require that fixed-width types exist; they're optional. In practice, every architecture you'd care to write C on (amd64, aarch64, ...) has fixed-width types.
When they are done, they can let me know who won and then I'll take that language a bit more seriously.
In the meantime I'm a humble coder sitting in the corner with Python + C + CUDA (plus some scheme and assembly), doing useless things like AI, robotics, IoT, etc.
Time to clear out the next two years so I can learn half a dozen special snowflake randos that contribue ZERO to the problems and projects I'm working on.