|
|
Subscribe / Log in / New account

Thoughts and clarifications

Thoughts and clarifications

Posted Sep 15, 2024 13:52 UTC (Sun) by pizza (subscriber, #46)
In reply to: Thoughts and clarifications by mb
Parent article: Whither the Apple AGX graphics driver?

> But if Rust code is refactored and the algorithms are not changed, you're 99% done when the compiler stops complaining.

That same argument applies to any statically-typed language, even (*gasp*) C.

Meanwhile, if you're not changing the algorithms/semantics/whatever in some way, why are you refactoring anything to begin with?


to post comments

Thoughts and clarifications

Posted Sep 15, 2024 14:02 UTC (Sun) by mb (subscriber, #50428) [Link]

> That same argument applies to any statically-typed language, even (*gasp*) C.

No. That is not true.
There's a very big difference between what you can encode in the C type system and what is possible with the Rust type system.

>if you're not changing the algorithms/semantics/whatever

That is not what I said.

Thoughts and clarifications

Posted Sep 15, 2024 14:51 UTC (Sun) by asahilina (subscriber, #166071) [Link]

> That same argument applies to any statically-typed language, even (*gasp*) C.

Not at all, not to the extent it does with Rust.

In C, if you change a structure to be reference-counted, the compiler does nothing to ensure you manage the reference counts correctly. In Rust it does.

In C, if you add a mutex to protect some data, the compiler does nothing to ensure you actually hold the mutex before accessing the data. In Rust it does.

In C, if you change the width or signedness of an integer member or variable, the compiler does nothing to ensure you actually update the type of any variables it's copied to or passed into, and it will happily let you truncate or convert integers, even with -Wall. In Rust it won't compile until you change all the types or add explicit casts, and you probably won't even need to touch any code that just creates temporary bindings since Rust has type inference for that and C does not (without extensions like __auto_type or weird macros).

In C, if you need to add cleanup code to a structure that was previously just freed with free(), you need to find all the sites where it is freed and change them to call a helper that does the extra cleanup manually. In Rust none of this code exists to begin with since freeing is automatic, you just implement the `Drop` trait on the struct to add extra cleanup code and you're done, no need to refactor anything at all.

Thoughts and clarifications

Posted Sep 16, 2024 10:36 UTC (Mon) by farnz (subscriber, #17727) [Link] (5 responses)

No; the degree to which you're done when things start compiling depends critically on how much is checked at compile time versus at run time. My experience over 8 years of doing Rust, and approximately 20 years doing C, is that Rust programs tend to have much more in the way of compile time checking than C programs, which in turn means that "it compiles" is a much stronger statement than in C (although not as strong as it tends to be in Idris or Agda).

A more interesting question is whether this will continue to hold as more people write Rust code - is this current behaviour an artifact of early Rust programmers tending to write more compiler-checked guarantees, or is this something that will continue to hold when the Rust programmer pool expands?

Thoughts and clarifications

Posted Sep 16, 2024 11:41 UTC (Mon) by pizza (subscriber, #46) [Link] (4 responses)

> A more interesting question is whether this will continue to hold as more people write Rust code - is this current behaviour an artifact of early Rust programmers tending to write more compiler-checked guarantees, or is this something that will continue to hold when the Rust programmer pool expands?

Personally, I strongly suspect the latter.

Current Rust programmers are self-selecting, in the upper echelon of skill/talent, and largely using Rust for Rust's sake. That is very much non-representative of the software development world as a whole. [1]

Rust will have its Eternal September, when relatively average-to-mediocre corporate body farms start cranking it out. At that point, "Rust Culture" goes out the window as the only "culture" that matters is what falls out of coroporate metrics<->reward mappings.

[1] So is C, for that matter. If I were to pull out a bad analogy, if effectively coding in C represents the top 10th percentile, Rust is currently the top 1%.

Thoughts and clarifications

Posted Sep 16, 2024 11:50 UTC (Mon) by pizza (subscriber, #46) [Link]

> Personally, I strongly suspect the latter.

Gaah, make that 'the former'. (As I hope was clear from the rest of the post)

Thoughts and clarifications

Posted Sep 16, 2024 13:21 UTC (Mon) by farnz (subscriber, #17727) [Link]

I disagree in part; I think it'll get worse than it is today, for the reasons you outline, but that it'll still remain a lot more true of Rust than it is of C.

I have access to a decent body of code written by a contract house (one of the big names for cheap outsourcing), and the comments make it clear that they used their cheap people, not their best people, to write the code. Of the four most common causes of issues refactoring that code, three are things that are compiler-checked in Rust:

  1. Assumptions about the sizes of arrays passed as arguments; where in C, I can pass a 2 element array to a function that expects a 4 element array, Rust either makes this a compile-time error (if the argument type is an array) or a runtime panic (if the argument type is reference to slice).
  2. Assumptions about wrapping of unsigned computations. C promotes unsigned bytes to signed int for calculations, and then does the computation, but there's chunks of this code that assume that every intermediate in a long sequence without storing to a known type remains unsigned (otherwise there's UB lurking when the intermediate exceeds INT_MAX).
  3. Failure to check all possible values of an enum, in large part because it's clear that the value got added after a module was "code complete", and nobody thought to add a default: or a special handler for this value.

Those all become panics or compile failures in Rust, leaving the errors in business logic (of which there are remarkably few) to deal with during refactoring.

And more generally, the biggest issue with cheap contractor C and C++ is the amount of code they write that depends on UB and friends being interpreted a particular way by the compiler, even in cases where there's no way to check that interpretation from code; Rust does seem to reduce this, even in beginner code, since unsafe is easy to find and be scared by.

Thoughts and clarifications

Posted Sep 22, 2024 18:24 UTC (Sun) by Rudd-O (guest, #61155) [Link] (1 responses)

> Rust will have its Eternal September, when relatively average-to-mediocre corporate body farms start cranking it out. At that point, "Rust Culture" goes out the window as the only "culture" that matters is what falls out of coroporate metrics<->reward mappings.

I haven't seen so far, at least in decades of me working in the industry, that eternal September has arrived to Haskell.

And I don't think that's going to happen. At least in Haskell. Maybe in Rust will. Maybe it won't.

There is a set of conceptual difficulties associated with learning any programming language, and it is not the same, depending on the language. Learning ATARI basic is one thing, (by the way that's the first language I learned). Learning Python is another Learning assembly is yet another Learning Haskell is another.

To pull the conversation away from the realm of language and just talk about concepts, pretty much any programmer can program using a stringly typed interface (which we all know leads to shitty code). But not every programmer is capable of learning the Haskell type system (I know I can't but ikcan understand how it leads to improved type safety and thus code quality).

All of this is to say that we're not all made equal. And because we're not all made equal, we are not all able to use the same tools. Just as we are not all able to wield a sledgehammer that weighs 30 pounds and break down a wall, so we are just as unequal to wield a specific programming language with skill and produce the results that one wants. Evolution does not stop at the neck.

But what do i know? Perhaps Haskell will get its eternal September? All i know is i can't learn it. Or at least I'm humble enough to admit that.

Thoughts and clarifications

Posted Sep 22, 2024 18:30 UTC (Sun) by Rudd-O (guest, #61155) [Link]

Addendum:

In case it's interesting for the readers here, the current firm I'm working at started the system that we are developing with Haskell. We had a lot of researchers that were super talented and were able to crank out what I consider pretty high quality code at the very beginning using nothing but Haskell.

The problem is that once you need to grow past 10 engineers, or in this case computer scientists, you can't. Finding 10 Haskell programmers in a Haskell conference is fairly easy. Finding the 11th to join your team when there's no conference going on is almost impossible. Hasklers are wicked smart, and because they're wicked smart, they're wicked rare.

So what did we do after that? We switched our system to Rust. Of course, the system continues to have the same type structure to the extent that it is possible, that it had back in the era when it started as Haskell. And all the Haskell programmers quickly adapted to using Rust because the type system in Rust is less complex than the type system in Haskell, so for them it was a downgrade. But we were able to quickly triple the amount of programmers that we had developing the system.

And the system continues to grow, and it has pretty high code quality for the standards of my career — I've seen code maybe 25 years? I routinely refactor the innards of the system without fearing that I'm going to break something somewhere else, somewhere deep in the system. I don't think I've ever felt so free to actually change the code without having the dread inside of me that it's going to catastrophically explode in production. Two years, and I have yet to put a bug in the system. That is almost completely magical.

Thoughts and clarifications

Posted Sep 22, 2024 18:19 UTC (Sun) by Rudd-O (guest, #61155) [Link]

> That same argument applies to any statically-typed language, even (*gasp*) C.

No. Not even close.

You can make a change in C struct, such that you're missing a member of the struct, and maybe the compiler will complain that you missed initialization somewhere of that struct member. That is true.

But if you add to an enum somewhere, which represents a new state of the object that you are using the enum on, and that enum is used in an if, case or select statement somewhere else, C will happily ignore that you've done this, and compile just fine. Then your code doesn't work.

In Rust, when you do this, generally a selector case statement equivalent, which is called match, will not compile, because it knows that you've added a different case that your code does not handle. Only after you have fixed every site where this enum is used, will it compile.

This simple thing prevents entire classes of logic errors in your code.

There are quite a few other ergonomic changes that the language has over other languages that existed before rust, which work in a similar way. to give you just one other example:

You change a C struct to have a new member that has a pointer to another type. You painstakingly change every place where that struct needs to be initialized so that your program will compile and run. This program is multi-threaded, like the kernel is. You run, your program, and it crashes. In this particular case, that new member that refers to a pointer to this other structure was used at the wrong time. This is due behavior that wasn't there before when the first structure did not have a pointer to the second one.

This is not possible in Rust. The compiler, in fact, the borrow checker in the compiler, keeps track of where every pointer is going and where every pointer is used. And will not let you compile the program if you use a pointer or a reference when you are not supposed to, when it's supposed to be dead or not initialized, or if the lifetime of the object taking a reference to that thing is a different lifetime, incompatible with the lifetime of the object pointed to by the pointer. It even knows when you are using data that you're not supposed to be using because you will have forgotten to grab a lock. And it will tell you you need to change how this is done. Try this, try this other thing, try this other thing. It gives you options.

This is so far ahead of anything that the C language does, that in fact could be construed as magic by Ken, Dennis, and Donald. You need to see it with your own eyes to believe it, but it is amazing.

On a personal note, this conversation on this particular thread has exposed to me the wide difference of perspective that C developers and Rust developers have. Having developed years of my life with both languages, I have the uncomfortable advantage of having perspective from both sides. But to me, it really does feel like we're arguing versus horses and carriages versus automobiles, or electric cars versus gas cars. I, too, thought Teslas were bullshit until I got on one, as a passenger, and the driver punched the pedal. Oh my god. It's a similar experience going from Python, or Go, or C, to Rust.

And I think that explains why a lot of people see what Rust developers say about the language, and then conclude, this must be a religion, or worse, a cult.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds