4 ms·
This post pretty much completely ignores the advantages of the borrow checker. I'm not talking about memory safety, which is it's original purpose. I'm talking
by IshKebab 1y ago
This post pretty much completely ignores the advantages of the borrow checker. I'm not talking about memory safety, which is it's original purpose. I'm talking about the fact that code that follows Rust's tree-style ownership pattern and doesn't excessively circumvent the borrow checker is more likely to be correct.
I don't think that was ever the intent behind the borrow checker but it is definitely an outcome.
So yes, the borrow checker makes some code more awkward than it would be in GC languages, but the benefits are easily worth it and they stretch far beyond memory safety.
- mikepurvis 1y agoThere's the practical end goal benefit of safer and more robust programs, but I think there's also the piece that pg talks about in Beating The Averages which is that learning how to cooperate with these conventions and think like there's the borrow checker there makes you a better programmer even when you return to languages that don't have it.
- al_fanta 1y ago> makes you a better programmer If a language is bad, but you must use it, then yes learn it. But, if the borrowchecker is a source of pain in Rust, why not andmit it needs work instead of saying that “it makes you better”? I’m not going to start writing brainfuck because it makes me a better programmer.
- lblume 1y agoI believe that codebases written in Rust, with borrow checking in mind, are often very readable and allow local reasoning better than most other languages. The potential hardness might not stem from "making people better programmers" but from "making programmers write better code, perhaps at the cost of some level of convenience".
- IshKebab 1y agoWe do admit it needs work. The issues the author highlights can be annoying, a smarter borrow checker could maybe solve them. The point is the borrow checker has already gone beyond the point where the benefits outweigh those annoyances. It's like... Static typing. Obviously there are cases where you're like "I know the types are correct! Get out of my way compiler!" but static types are still vastly superior because of all the benefits they convey in spite of those occasional times when they get in the way.
- creata 1y ago> The issues the author highlights can be annoying, a smarter borrow checker could maybe solve them I don't think a smarter borrow checker could solve most of the issues the author raises. The author wants borrow checking to be an interprocedural analysis, but it isn't one by design. Everything the borrow checker knows about a function is in its signature.
- estebank 1y agoMaking partial borrows to be expressable in method definitions would allow the design pattern to be expressed without breaking the current lifetime evaluation boundary. Allowing the borrow checker to peek inside of the body of local methods for the purposes of identifying partial borrows would fundamentally break the locality of the borrow checker, but I think that as long as that analysis is only extended to methods on the local trait impl, it could be done without too much fanfare. These two things would be relaxations of the borrow checker rules, making it smarter, if you will.
- deleted 1y ago[deleted]
- ameliaquining 1y agoFixing the get_default example wouldn't require interprocedural analysis. (It requires Polonius, which, yeah, has taken a long time to ship.)
- eigenspace 1y agoHe's not ignoring them. The point of the article is that the author doesn't experience those things as concrete advantages for them. Like sure, there are advantages to those things, but the author says he doesn't feel it's worth the trouble in his experience for the sorts of code he's writing.
- nine_k 1y agoOne good RCE in production could alter this perception quite a bit. "The mosquito repellent is useless, I see too few mosquitos around me anyway."
- eigenspace 1y agoThe author is a bioinformatician writing scientific software, and often switching back and forth between Rust, Julia, and Python. His concerns and priorities are not the same as people doing systems-level programming.
- nine_k 1y agoMaybe Rust, a systems language, is just a wrong tool for bioinformatic tasks. Go, Java, Typescript, Ocaml, Scala, Haskell easily offer a spectrum from extreme simplicity to extreme expressiveness, with good performance and library support, but without needing to care about memory allocation and deallocation. (Python, if you use it as the frontend to pandas / polars, also counts.)
- eigenspace 1y agoI know you're not supposed to berate people here for not reading TFA, but this really feels like a case where it's very frustrating to engage with you because you really should read TFA.
- nine_k 1y agoWell, I have read TFA. The author may have a point in the idea of borrowing record fields separately. It is possible if we assume that the fields are completely orthogonal and can be mutated independently without representing an incorrect state. It would be a good option to have. But a doubly-linked list (or graph) just can't be safely represented in the existing reference semantics. Dropping a node would lead to a dangling pointer, or several. An RDBMS can handle that ("on delete set null"), because it requires a specific way to declare all such links, so that they can be updated (aka "foreign keys"). A program usually does not (Rc / Arc or shared_ptr provide a comparable capability though). Of course a bidirectional link is a special case, because it always provides a backlink to the object that would have a dangling pointer. The problem is that the borrow checker does not know that these two pointers belonging to different structs are a pair. I wish Rust had direct support for such things, then, when one end of the bidirectional link dies, the borrow checker would unset the pointer on the reciprocal end. Linking the objects together would also be a special operation that atomically sets both pointers. In a more general case, it would be interesting to have a way to declare some invariants over fields of a struct. A mutual pair of pointers would be one case, allowing / forbidding to borrow two fields at once would be another. But we're far from that. Special-casing some kinds of pointers is not unheard of; almost every GC-based language offers weak references (and Java, also Soft and Phantom references). I don't see why Rust could not get a BidirectionalRef of sorts. Until then, Arc or array with indexes seem to be the only guaranteed memory-safe approaches. Also, in the whole article I could not find a single reason why the author chose Rust, but I suppose it's because of its memory efficiency, considering the idea of keeping large graphs in RAM. Strictly speaking, Go could be about as efficient, but it has other inflammation points. C++... well, I hope Rust is not painful enough to resort to that.
- ActorNightly 1y ago> is more likely to be correct. This is a moot statement. Here is a thought experiment that demonstrates the pointlessness of languages like Rust in terms of correctness. Lets say your goal is ultimate correctness - i.e for any possible input/inital state, the program produces a known and deterministic output. You can chose 1 of 2 languages to write your program in: First is standard C Second is an absolutely strict programming language, that incorporates not only memory membership Rust style, but every single object must have a well defined type that determines not only the set of values that the object can have, but the operations on that object, which produce other well defined types. Basically, the idea is that if your program compiles, its by definition correct. The issue is, the time it takes to develop the program to be absolutely correct is about the same. In the first case with C, you would write your program with carefully designed memory allocation (something like mempool that allocates at the start), you would design unit tests, you would run valgrind, and so on. In the second case, you would spend a lot more time carefully designing types and operations, leading to a lot of churn of code-compile-fix error-repeat, taking you way longer to develop the program. You could argue that the programmer is somewhat incompetent (for example, forgets to run valgrind), so the second language will have a higher change of being absolutely correct. However the argument still holds - in the second language, a slightly incompetent programmer can be lazy and define wide ranging types (similar to `any` in languages like typescript), leading to technical correctness, but logic bugs. So in the end, it really doesn't matter which language you chose if you want ultimate correctness, because its all up to the programmer. However, if your goal is rapid prototyping, and you can guarantee that your input is constrained to a certain range, and even though out of range program will lead to a memory bug or failure of some sort, programming in something like C is going to be more efficient, whereas the second language will force you write a lot more code for basic things.
- ashdksnndck 1y agoThis claim makes some sense to me if your development life cycle is: write and compile once, never touch again. Working at a company with lots of systems written by former employees running in production… the advantages of Rust become starkly obvious. If it’s C++, I walk on eggshells. I have to become a Jedi master of the codebase before I can make any meaningful change, lest I become responsible for some disaster. If it’s Rust, I can just do stuff and I’ve never broken anything. Unit tests of business logic are all the QA I need. Other than that, if it compiles it works.
- jandrewrogers 1y ago> code that follows Rust's tree-style ownership pattern This is a pretty important qualification. Most low-level systems code doesn't and can't have this ownership structure. It provides a reason why Rust has more traction replacing code that could have been written in Java rather than e.g. C++ in the domains where C++ excels (like database engines).