Sadly most people don't agree with this

I have been seeing hatred on this forum towards Rust since long time. Initially it didn't make any kind of sense. Only after actually trying to learn it did I understand the backlash.

It actually is so difficult, that most people might never be able to be proficient in it. Even if they tried. Especially coming from the world of memory managed languages. This creates push back against any and every use, promotion of Rust. The unknown fear seem to be that they will be left behind if it takes off.

I completed my battles with Rust. I don't even use it anymore (because of lack of opportunities). But I love Rust. It is here to stay and expand. Thanks to the LLMs and the demand for verifiability.

I find Rust much easier to write than C. Its types let me be reasonably sure I’ve written appropriate code before I even get to the point of running tests, and I don’t have to memorize the flow of the whole program to have that assurance.

For instance,

  struct Feet(i32);
  struct Meters(i32);
  
  fn hover(altitude: Meters) {
      println!("At {} meters", altitude.0);
  }
  
  fn main() {
      let altitude1 = Meters(16);
      hover(altitude1);
      let altitude2 = Feet(16);
      hover(altitude2);
  }
This fails at build time with:

   12 |     hover(altitude2);
      |     ----- ^^^^^^^^^ expected `Meters`, found `Feet`
      |     |
      |     arguments to this function are incorrect
Guaranteeing that I’ve never mixed units means I don’t have to worry about parking my spacecraft at 1/3 the expected altitude. Now I can concentrate on the rest of the logic. The language has my back on the types so I never have to waste brain cycles on the bookkeeping parts.

That’s one example. It’s not unique to Rust by a long shot. But it’s still a vast improvement over C, where that same signed 32 bit data type is the number of eggs in a basket, the offset of bytes into a struct, the index of an array, a UTF-8 code point, or whatever else.

This really shows up at refactoring time. Move some Rust code around and it’ll loudly let you know exactly what you need to fix before it’s ready. C? Not so much.

All you're doing is passing an argument of the incorrect type to your function. The exact same thing fails to compile in C:

``` #include <stdio.h>

typedef struct { int value; } Feet;

typedef struct { int value; } Meters;

void hover(Meters altitude) { printf("At %i meters\n", altitude.value); }

int main() { Meters altitude1 = {.value = 16}; hover(altitude1); Feet altitude2 = {.value = 16}; hover(altitude2); } ```

``` error: passing 'Feet' to parameter of incompatible type 'Meters' 20 | hover(altitude2); ```

Coming from a dynamically typed language (Python, etc), this might seem like a revelation, but its old news since the dawn of programming computers. A C language server will pick this up before compile time, just like `rust-analyzer` does: `argument of type "Feet" is incompatible with parameter of type "Meters"`.

Did you not know this? I feel like a lot of people on message boards criticizing C don't know that this would fail to compile and the IDE would tell you in advance...

[deleted]

People use header libraries as they treat languages like C and C++ as if they were scripting languages.

right and in C++ you have amazing zero-overhead units libraries

That’s not a good example. Wrapping an integral value into a struct works exactly the same in C and (https://stackoverflow.com/questions/40629989/pros-and-cons-o...)

In C++ you can even add overloaded operators to make using math on such structs ergonomical.

Compilers know of the idiom, and will optimize the struct away.

It would've saved the Mars Climate Orbiter: https://en.wikipedia.org/wiki/Mars_Climate_Orbiter

> An investigation attributed the failure to a measurement mismatch between two measurement systems: SI units (metric) by NASA and US customary units by spacecraft builder Lockheed Martin.[3]

That was only the proximate cause, the ultimate cause was cultural. As complex systems and efforts run into problems, it is trivial to blame the unit conversion when they had been ignoring people for months who had concerns [0]

> ... ground controllers ignored a string of indications that something was seriously wrong with the craft's trajectory, over a period of weeks if not months. But managers demanded that worriers and doubters "prove something was wrong," even though classic and fundamental principles of mission safety should have demanded that they themselves, in the presence of significant doubts, properly "prove all is right" with the flight

Dropping units on the NASA side also was problematic but really culture was the cause of the actual crash.

[0] https://spectrum.ieee.org/why-the-mars-probe-went-off-course

That's technically true, but if NASA's API accepted arguments in Meters instead of int32_t (or whatever the equivalent was in the language they used), then it would've been instantly obvious that the controller code that Lockheed Martin wrote was using the wrong datatype.

Do we know how the code was called, was it linked in or was it via IPC (the latter seems most likely to me, and then the question is does the IPC framework support a rich type system)?

That was exactly what I was thinking of when I wrote that.

But also think of how many libc functions take multiple ints or multiple chars in various orders. You can get carried away with typing, i.e. by having a separate type for everything*. Still, imagine you’re writing, say, a hypothetical IDE device driver and had separate types for BlockNumber and ByteInBlock so that it’s impossible to transpose read(byte_offset,block) instead of read(block,byte_offset), even if those are really the same kind of numbers.

That kind of thing makes a gazillion bugs just vanish into the ether.

I sometimes think about a related issue: suppose you have a function whose n parameters have n different types. Should the programmer be required to provide those parameters in a specific order? There's no ambiguity.

There appears to be some tension between different conveniences you might afford yourself. If you have read(offset: offsetTypeForRead, address: addressTypeForRead), you can catch when someone accidentally passes an address where the offset should be and an offset where the address should be.

Or, you can say "hey, I'm always adding the offset to the address; it doesn't matter which one gets passed first" and relieve the programmer of needing to know the order in which two semantically distinct variables get passed to `read`.

But if you do provide that convenience -- and it's not unintuitive at all; there really is only one valid interpretation of a combination of an address and an offset, regardless of the order you mention them in -- you lose some of the safety that you wanted from the types. If your variables are declared correctly, everything is fine. If there's a mistake in declaring them, you'll wave through incorrect calls to `read` that would have been caught before.

Huh, that’s an interesting point, and I’d have to think on that. There are still plenty of cases where ordering would matter, like subtract(a,b), unless you go whole hog and define that like

  fn sub(a:LeftOp, b:RightOp)
but that seems redundant. There are still plenty of other cases where I could your idea being useful. Like I always forget whether (in Python) it’s json.dump(file, data) or dump(data, file). Ultimately, should it matter? I’m passing a file handle and an object, and it’s unambiguous how those two args relate to the task at hand.
[deleted]

A bit clunky but more foolproof is something like:

ide_read ( &(struct ide_loc) { .byte_offset = 10, .block = 20 } )

How does this scheme of yours work with m/s and seconds.

IIUC, rust would NOT let you do a type checked m/s * s => m, so using the type system for these kinds of games is silly and dangerous (I presume you would have to do the dumb thing and typeconvert to the same type -- e.g.

    (m) (speed * ((m/s) seconds))
to do multiplication which means you're inserting unscientific and reader-confusing type conversions all over the place)

That's no problem in Rust. You can have a Speed type and a Time type, and define the multiplication operator over them to return a Length value. In fact, there's already a library which does exactly that, with tons of predefined SI units: https://docs.rs/uom/latest/uom/

And the exact same thing can be done in C++.

Related library Sguaba [1] from Helsing AI written by Jon Gjengset it allows you to define coordinate systems on the type level and safe conversion and calculation with them.

[1] https://github.com/helsing-ai/sguaba

I love that sort of thing. It so much easier to get up and running with plausible results when it’s all but impossible to misuse the API. “Darn it, it’s making me cast Celsius to Meters before I call this function. Hey, wait, that can’t be right…”

I'm very confused, explain how this is not the case with C?

I haven't written rust, but my impression is the benefit is more about deeper introspection of things like lifetime than basic typesafety, which already exists in C/C++ (and is likewise occasionally bypassed for convenience, so I wonder how often the same is done for Rust)

This would get an error message in C, what are you talking about?

Structs in C are type safe as well.

Nah, you’re just not good enough. I for example would have never made that mistake when calling hover(int32_t) in C. And on the off chance I did, my reviewers would have caught such a simple mistake because they too are excellent C developers.

/s

My jaw reflexively clenched.

> Especially coming from the world of memory managed languages.

If people from that world complain about Rust, I surely wouldn't want them around a C codebase.

There's nothing wrong about memory-managed languages, if you don't need to care about memory. But being unfamiliar with memory and complaining about the thing that help you avoid shooting your foot isn't something that inspires trust.

The hardship associated with learning rust isn't going to go away if they do C instead. What's going to happen instead is that bugged code will be written, and they will learn to associate the hardship with the underlying problem: managing memory.

> It actually is so difficult, that most people might never be able to be proficient in it. Even if they tried

I think this is more true of C than it is of Rust if the bar is "code of sufficient quality to be included in debian"

Anyone capable of programming and willing to invest enough time will be able to learn Rust, in my opinion.

It might take some people months rather than days, but I think that is a desirable outcome.

Important low level software should be written by competent developers willing to invest the effort.

The effort should rather be invested into learning how underlying hardware works and introducing redundancy and observability into the system.

That's non-sequitur.

The problem here is that C is too basic, dated, with inadequate higher-level abstractions, which makes writing robust and secure software extra difficult and laborious. "Learning underlying hardware" doesn't solve that at all.

Debian supports dozens of architectures, so it needs to abstract away architecture-specific details.

Rust gives you as much control as C for optimizing software, but at the same time neither Rust nor C really expose actual underlying hardware (on purpose). They target an abstract machine with Undefined Behaviors that don't behave like the hardware. Their optimisers will stab you in the back if you assume you can just do what the hardware does. And even if you could write directly for every logic gate in your hardware, that still wouldn't help with the fragility and tedium of writing secure parsers and correct package validation logic.

> It actually is so difficult, that most people might never be able to be proficient in it. Even if they tried.

That could also be applied to C and C++ …

Assuming you're right about the language being too hard for most people, the outcome I'd expect given the history of computing is that the language will fail or be stuck in a niche

Time and time again, theoretically worse solutions that are easily accessible win