8 ms·
You’re right that Rust is a ball of complication. I am a fan of Rust but it’s definitely a terse language. However there are definitely signs that they have t
by dijit 1y ago
You’re right that Rust is a ball of complication.
I am a fan of Rust but it’s definitely a terse language.
However there are definitely signs that they have thought about making it as readable as possible (by omitting implicit things unless they’re overwritten, like lifetimes).
I’m reminded also about a passage in a programming book I once read about “the right level of abstraction”. The best level of abstraction is the one that cuts to the meat of your problem the quickest - spending a significant amount of time rebuilding the same abstractions over and over (which, is unfortunately often the case in C/C++) is not actually more simple, even if the language specifications themselves are simpler.
C codebases in particular, to me, are nearly inscrutable unless I spend a good amount of time unpicking the layers of abstractions that people need to write to make something functional.
I still agree that Rust is a complex language, but I think that largely just means it’s frontloading. a lot of the understanding about certain abstractions.
- uecker 1y agoI do not really agree with respect to C. I often have to deal with C code written by unexperienced programmers. It is always relatively easy to refactor it step by step. For C++ this is much more painful because all the tools invented to make the code look concise make it extremely hard to follow and change. From the feature set, I would say that Rust is the same (but I have no experience refactoring Rust code).
- AstralStorm 1y agoIn personal experience, the much stronger and composite type model in Rust makes it easier to refactor. Adding features in particular is a breeze and automatically the compiler/language will track for you the places that use only old set of traits. Tooling is still newer though and needs polish. Generic handling is interesting at times and there are related missing features for that in the language, vis a vis specializations in particular. Basic concurrency handling is also quite different in Rust than other languages, but thus usually safer.
- uecker 1y agoI am wondering about this. In C the nice thing is that you usually change things locally. A line of code does not depend on a million other things. I can't quite see how this works in Rust... At least in C++, template, overloading, etc. can make a single line depend on a lot of things.
- Expurple 1y ago> A line of code does not depend on a million other things. But it does! To qoute my top-level comment: > What about about race conditions, null pointers indirectly propagated into functions that don't expect null, aliased pointers indirectly propagated into `restrict` functions, and the other non-local UB causes? In other words: you set some pointer to NULL, this is OK in that part of your program, but then the value travels across layers, you've skipped a NULL check somewhere in one of those layers, NULL crosses that boundary and causes UB in a function that doesn't expect NULL. And then that UB itself also manifests in weird non-local effects! Rust fixes this by making nullability (and many other things, such as thread-safety) an explicit type property that's visible and force-checked on every layer. Although, I agree that things like macros and trait resolution ("overloading") can be sometimes hard to reason about. But this is offset by the fact that they are still deterministic and knowable (albeit complex)
- uecker 1y agoThis is true to some degree, but not really that much in practice. When refactoring you just add assertions for NULL (and the effect of derefencing a NULL in practice is a trap - that it UB in the spec is completely irrelevant. in fact it helps because it allows compilers to turn it a trap without requiring it on weak platforms). Restrict is certainly dangerous, but also rarely used and a clear warning sign, compare it to "unsafe". The practical issues in C are bounds checking and use-after-free. Bounds checking I usually refactor into safe code quickly. Use-after-free are the one area where Rust has a clear advantage.
- Expurple 1y ago
- bryanlarsen 1y ago> but I have no experience refactoring Rust code But you're willing to write many comments complaining that Rust is hard to refactor. Rust is the easiest language to refactor with I've ever worked in, and I've used a couple dozen or so. When you want to change something, you change it, and then fix compiler errors until it stops complaining. Then you run it, and it works the first time you run it. It's an incredible experience to worry so little about unknown side-effects.
- Dylan16807 1y ago> But you're willing to write many comments complaining that Rust is hard to refactor. Their refactoring comments look focused on C versus C++ to me, with a bit of guessing Rust is like C++ in a way that is clearly labeled as speculation. So I don't see the problem with anything they said about refactoring.
- bsder 1y ago> Rust is the easiest language to refactor with I've ever worked in Oh? So how do you refactor a closure into a named function in Rust? I have found this to be of the most common failure modes that makes people want to punch the monitor. (Context: in almost all programming languages, a closure is practically equivalent to an unnamed function--refactoring a closure to a named function tends to be pretty straightforward. This isn't true for Rust--closures pick up variable lifetime information that can be excruciatingly difficult to unwind to a named function.)
- bryanlarsen 1y agoSet the type of all the parameters to your new function to bool, then compile. The compiler error will tell you, "Error: you passed a foo<'bar, baz> instead of a bool". Then you change the type in the function's parameter to "foo<'bar, baz>".
- afdbcreid 1y agoYou invoke the assist of rust-analyzer that does that automatically.
- 1y ago
- anon-3988 1y ago> I do not really agree with respect to C. I often have to deal with C code written by unexperienced programmers. It is always relatively easy to refactor it step by step. Really? How confident are you to change a data structure that uses an array with linear search lookup to a dictionary? Or a pointer that now is nullable (or is now never null)? Unless you have rigorous test or the code is something trivial, this would be a project of its own. I am pretty sure I can swap out the implementation of the dictionary in the rust compiler and by the time the compilation issues are worked out, the code would be correct by the end of it (even before running the tests)
- sigi64 1y agoI can compare programming and refactoring large code bases in C and C++, Rust, and Python, from system and parsing and protocol libraries to asynchronous multi-threaded servers in the mentioned languages. Refactoring Rust projects is clearly the easiest because the compiler and type system ensure the program is correct at least in terms of memory access and shared resource access. It doesn't protect me from memory leaks and logical errors. But since Rust has a built-in testing framework, it's quite easy to prepare tests for logical errors before refactoring. C/C++ refactoring is a nightmare - especially in older projects without modern smart pointers. Every change in ownership or object lifetime is a potential disaster. In multi-threaded applications it's even worse - race conditions and use-after-free bugs only manifest at runtime, often only in production. You have to rely on external tools like Valgrind or AddressSanitizer. Python has the opposite problem - too much flexibility. You can refactor an entire class, run tests, everything passes, but then in production you discover that some code was dynamically accessing an attribute you renamed. Type hints help, but they're not enforced at runtime. Rust forces you to solve all these problems at compile time. When you change a lifetime or ownership, the compiler tells you exactly where you need to fix it. This is especially noticeable in async code - in C++ you can easily create a dangling reference to a stack variable that an async function uses. In Rust, it simply won't compile. The only thing where Rust lags a bit is compilation speed during large refactors. But that's a small price to pay for the certainty that your code is memory-safe. Another area where Rust absolutely excels is when using AI agents like Claude Code. It seems to me that LLMs can work excellently with Rust programs, and thanks to the support of the type system and compiler, you can get to functional code quickly. For example, Claude Code can analyze Rust programs very well and generate documentation and tests. I think Rust with an AI agent has the following advantages: Explicit contract - the type system enforces clear function interfaces. The AI agent knows exactly what a function expects and what it returns. Compiler as collaborator - when AI generates code with an error, it gets a specific error message with the exact location and often a suggested solution. This creates an efficient feedback loop. Ownership is explicit - AI doesn't have to guess who owns data and how long it lives. In C++ you often need to know project conventions ("here we return a raw pointer, but the caller must not deallocate it"). Fewer implicit assumptions - in Python, AI can generate code that works for specific input but fails on another type. Rust catches these cases at compile time.