Essential insights from Hacker News discussions

Unexpected productivity boost of Rust

This discussion revolves around the perceived benefits and drawbacks of Rust's type system and its strictness, often contrasting it with Python, TypeScript, and Zig. Several key themes emerge:

The Value of Strong Type Systems and Static Analysis

A prominent theme is the appreciation for languages with robust type systems that catch errors at compile time, preventing them from surfacing at runtime. Rust's type system, particularly its ownership and borrowing rules, is highlighted as a significant contributor to this.

  • "The benefits realized can be mostly attributed to strong type checking." (jvanderbot)
  • "I don't agree that Python can be saved by a type checker. The language itself is flawed irreversibly, as well as its ecosystem. It's a big big mess. Can't build reliable software in Python." (koakuma-chan)
  • "If you want to not reliably know anything about the code, sure. But if you want to have useful knowledge, using a stochastically unreliable tool isn't going to cut it." (bigstrat2003)
  • "I find Rust and F#'s typesystems most enjoyable when refactoring code. Fearless refactoring is the right expression here." (raphinou)
  • "The main difference between Rust and other languages with a Standard ML influenced type system is that Rust has features that can let you get executive sign off for switching languages." (bryanlarsen)
  • "Rust's marker traits (Send, Sync, etc.) + RAII is really what makes it so magical to me." (VWWHFSfQ)
  • "The language tries hard not to let you write 'you're holding it wrong' bugs, whereas Zig tends to choose simplicity and explicitness instead." (NobodyNada)

Python's Pitfalls and the Role of Type Hints

There's a strong critique of Python, with many users expressing frustration with its dynamic typing, runtime errors, and what they perceive as a messy ecosystem. While type hints (like those provided by mypy and pyright) are acknowledged as helpful, they are seen as insufficient to fully mitigate Python's inherent weaknesses compared to languages with stronger static typing.

  • "I encourage every one to at least stop writing code in Python." (koakuma-chan)
  • "Koakuma-chan: Do you know a language other than Rust that has alternative for docs.rs? In JavaScript and Python they never bother to have any documentation or reference, one notable example that gets me frustrated these days is the openai SDK for TypeScript. There is no documentation for it. I have to go look at the source code to figure out what the hell goes on."
  • "koakuma-chan: I don't agree that Python can be saved by a type checker. The language itself is flawed irreversibly, as well as its ecosystem. It's a big big mess. Can't build reliable software in Python." (koakuma-chan)
  • "Koakuma-chan: If you know some secret behind building reliable software in a programming language without types, with nulls, and with runtime exceptions, I'm all ears. I admit that a blanket statement "can't build reliable software" is going overboard, but the intention was to be dramatic, not factually correct. You can probably build reliable software in Python if you write everything from scratch, but I wouldn't want to do that to myself. I would rather use a programming language that has a type system, etc, and a better cultured ecosystem."
  • "guibarbill: Sorry, it's not even close to Rust. Or even C# or Java. It can't provide the same 'fearless refactoring'. It is better than being blind/having to infer everything manually. That's not saying much."
  • "deathanatos: I've not tried Pyright, but mypy on any realistic, real-world codebase I've thrown at it emits ~80k errors. It's hard to get started with that."
  • "mixmastamyk: I write very reliable Python every day. A lot faster than the Rust straitjacket too."
  • "ninetyninenine: It can't be 100% saved, but like the OP said it's 80% saved. It's not true you can't build reliable software in python. People have. There's proof of it everywhere. Tons of examples of reliable software written in python which is not the safest language. I think the real thing here is more of a skill issue. You don't know how to build reliable software in a language that doesn't have full type coverage. That's just your lack of ability."

The Nuances and Challenges of Async Rust

A significant portion of the discussion delves into the complexities of asynchronous programming in Rust, particularly around futures, Send and Sync traits, and the management of mutexes. While Rust's type system aims to provide safety, users highlight areas where it can be challenging to reason about or where certain patterns can lead to subtle bugs.

  • "The compiler doesn't know anything about threads. The compiler knows the Future doesn't implement the Send trait because MutexGuard is not Send and it crosses await points." (veber-alex)
  • "The compiler knows the Future doesn't implement the Send trait because MutexGuard is not Send and it crosses await points." (veber-alex)
  • "One caveat though - using a normal std Mutex within an async environment is an antipattern and should not be done - you can cause all sorts of issues & I believe even deadlock your entire code." (vlovich123)
  • "maplant: > using a normal std Mutex within an async environment is an antipattern and should not be done This is simply not true, and the tokio documentation says as much: 'Contrary to popular belief, it is ok and often preferred to use the ordinary Mutex from the standard library in asynchronous code.'"
  • "dilawar: True. I used std::mutex with tokio and after a few days my API would not respond unless I restarted the container. I was under the impression that if it compiles, it's gonna just work (fearless concurrency) which is usually the case."
  • "sunshowers: The big thing is that futures are passive, so any future can be cancelled at any await point by dropping it or not polling it any more. So if you have a situation like this, which in my experience is a very common way to use mutexes:" ```rust let guard = mutex.lock().await; // guard.data is Option, Some to begin with let data = guard.data.take(); // guard.data is now None

    let new_data = process_data(data).await; guard.data = Some(new_data); // guard.data is Some again `` "Then you could cancel the future at the await point in between while the lock is held, and as a result guard.data will not be restored to Some." * "Tokio MutexGuards are Send, unfortunately, so they are really prone to cancellation bugs." (sunshowers) * "The generally recommended alternative is message passing/channels/'actor model' where there's a single owner of data which ensures cancellation doesn't occur -- or, at least that if cancellation happens the corresponding invalid state is torn down as well. But that has its own pitfalls, such as starvation." (sunshowers) * "benmmurphy: original poster was 'lucky' that he was using a work stealing engine. if the engine was not moving tasks between threads then i think the compiler would have been happy and he could have had the fun of debugging what happens when the same thread tries to lock the same mutex twice. the rust compiler won't save you from this kind of bug." * "synalx: More the latter. The lock guard is notSend, so holding it across the await point makes theimpl Futurereturned by the async function also notSend. Therefore it can't be passed to a scheduler which does work stealing, since that scheduler is typed to require futures to beSend`."

Zig's Design Philosophy and Potential Pitfalls

The discussion touches upon Zig, particularly highlighting a perceived flaw in its error handling or type system where an if statement comparing an error with the global error set compiles, even though it's intended to be compared against a specific error set. This is contrasted with Rust's strictness.

  • "It's just so brittle. How can anyone think this is a good idea?" (veber-alex, regarding Zig's error handling example)
  • "The error presented in this example would not be written by any zig developer." (arwalk)
  • "veber-alex: But why? If I just need to check for 1 specific error and do something why do I need a switch? In Rust you have both "match" (like switch) and "if let" which just pattern matches one variant but both are properly checked by the compiler to have only valid values."
  • "arwalk: The example is a bit dubious. Sure, it compiles just fine, because the author is not using errors properly in zig. Here, he uses the global error set with error. AccessDenid, and as stated, it compiles just fine because when you reach the global error set, it's integers all the way down."
  • "veber-alex: Why does the compiler decay the FileError and the global error set to an integer? If they were unique types, the if statement would not have compiled."
  • "empath75: All examples of this type come down to 'the user made a mistake,' but that is kind of the the entire point. It is not possible to make that mistake in rust."
  • "NobodyNada: Rust's top priority is program correctness -- the language tries hard not to let you write 'you're holding it wrong' bugs, whereas Zig tends to choose simplicity and explicitness instead."
  • "tialaramex: I like Rust's break 'label value. It's very rarely the right thing, but sometimes, just sometimes, it's exactly what you needed and going without is very annoying. However IIUC for some time several key Rust language people hated this language feature, so it was blocked from landing in stable Rust. If Rust was an auteur language, one person's opinion could doom that feature forever."

The Utility and Ergonomics of as Casting in Rust

A minor but recurring thread is the debate around Rust's as operator for type casting. While some find it indispensable for its brevity and utility in specific scenarios (like truncating numbers), others view it as an unsafe and unidiomatic feature that should be replaced with safer alternatives.

  • "The only issue with it is that Rust's aversion to half-baked code means that you can't have 'partially working code' during the refactor: you either finish it or bail on it, without the possibility to have inconsistent codebase state. This is particularly annoying for exploratory code." (estebank)
  • "kouteiheika: Like, how can anyone think that having a cast that can implicitly convert from any numeric type to any other in conjunction with pervasive type inference is a good idea, like Rust's terrible as operator? (I once spent a whole day debugging a bug due to this.)"
  • "veber-alex: You are right, but it doesn't mean we can't complain about it :) As a side note, I hate the as cast in Rust. It's so brittle and dangerous it doesn't even feel like a part of the language. It's like a JavaScript developer snuck in and added it without anyone noticing."
  • "bigstrat2003: As a Rust user, I hope that 'as' never goes away. It's way too useful to get rid of, e.g. deliberately truncating numbers to a smaller number of bits. Can you do that in other ways? Absolutely. But they are all less ergonomic than 'as'."
  • "JoshTriplett: I'm hoping that we provide ergonomic alternatives for all the individual use cases. The brevity of as is a major reason we haven't removed it yet."
  • "Rusky: A method call like .trunc() is still going to be abysmally less ergonomic than as. It relies on inference or turbofish to pick a type, and it has all the syntactic noise of a function call on top of that."

The Trade-off Between Strictness and Explanatory Power (and LLMs)

The discussion also touches on the broader philosophical trade-off between language strictness that aims for correctness versus languages that might be more forgiving but require more manual oversight. The potential role of LLMs in bridging this gap is also brought up.

  • "9question1: These days isn't the solution to this just "ask " to read the code and write the documentation"?"
  • "koakuma-chan: Yes, you can have Claude Code go through the code and make an .md file with documentation for all the public APIs. I do that for everything that doesn't provide llms.txt."
  • "bigstrat2003: If you want to not reliably know anything about the code, sure. But if you want to have useful knowledge, using a stochastically unreliable tool isn't going to cut it."
  • "NobodyNada: Rust's top priority is program correctness -- the language tries hard not to let you write 'you're holding it wrong' bugs, whereas Zig tends to choose simplicity and explicitness instead. That difference in design goals is the whole point of the article, not a reason to dismiss it."
  • "Cthulhu_: It's a tradeoff, reminds me of Go not compiling if you have an unused variable; the strictness is a feature and basically locks out sloppy / half baked code. I personally see Rust as an ideal "second system" language, that is, you solve a business case in a more forgiving language first, then switch (parts) to Rust if the case is proven and you need the added performance / reliability."