Rust Is Hard, Or: The Misery of Mainstream Programming

This page summarizes the projects mentioned and recommended in the original post on news.ycombinator.com

Our great sponsors
  • Scout APM - Less time debugging, more time building
  • SonarLint - Clean code begins in your IDE with SonarLint
  • SaaSHub - Software Alternatives and Reviews
  • v-mode

    🌻 An Emacs major mode for the V programming language.

    I really like the idea of something like vlang (https://vlang.io), as I've tried multiple times to find the motivation to learn Rust, but always end up going back to golang for the simplicity.

  • ocaml

    The core OCaml system: compilers, runtime system, base libraries

    OCaml[1] is what you are looking for. You also might want to look at Roc[2], even though that's not released yet.

    1. https://ocaml.org/

  • Scout APM

    Less time debugging, more time building. Scout APM allows you to find and fix performance issues with no hassle. Now with error monitoring and external services monitoring, Scout is a developer's best friend when it comes to application development.

  • triple-buffer

    Implementation of triple buffering in Rust

    Rust marks cross-thread shared memory as immutable in the general case, and allows you to define your own shared mutability constructs out of primitives like mutexes, atomics, and UnsafeCell. As a result you don't get rope to hang yourself with by default, but atomic orderings are more than enough rope to devise incorrect synchronizations (especially with more than 2 threads or memory locations). To quote an earlier post of mine:

    In terms of shared-memory threading concurrency, Send and Sync, and the distinction between &T and &Mutex and &mut T, were a revelation when I first learned them. It was a principled approach to shared-memory threading, with Send/Sync banning nearly all of the confusing and buggy entangled-state codebases I've seen and continue to see in C++ (much to my frustration and exasperation), and &Mutex providing a cleaner alternative design (there's an excellent article on its design at http://cliffle.com/blog/rust-mutexes/).

    My favorite simple concurrent data structure is https://docs.rs/triple_buffer/latest/triple_buffer/struct.Tr.... It beautifully demonstrates how you can achieve principled shared mutability, by defining two "handle" types (living on different threads), each carrying thread-local state (not TLS) and a pointer to shared memory, and only allowing each handle to access shared memory in a particular way. This statically prevents one thread from calling a method intended to run on another thread, or accessing fields local to another thread (since the methods and fields now live on the other handle). It also demonstrates the complexity of reasoning about lock-free algorithms (https://github.com/HadrienG2/triple-buffer/issues/14).

    I find that writing C++ code the Rust way eliminates data races practically as effectively as writing Rust code upfront, but C++ makes the Rust way of thread-safe code extra work (no Mutex unless you make one yourself, and you have to simulate &(T: Sync) yourself using T const* coupled with mutable atomic/mutex fields), whereas the happy path of threaded C++ (raw non-Arc pointers to shared mutable memory) leads to pervasive data races caused by missing or incorrect mutex locking or atomic synchronization.

  • smartstring

    Compact inlined strings for Rust.

    > If you have a long-running async function, then pass parameters by value! If you have a polymorphic async function, then return your result in a Box.

    I've taken to making heavy use of the smallvec and smartstring crates for this. Most lists and strings are small in practice. Using smallvec / smartstring lets you keep most clone() calls allocation-free. This in turn lets you use owned objects, which are easier to reason about - for you and the borrow checker. And you keep a lot of the performance of just passing around references.

    I tried to use async rust a couple of years ago, and fell on my face in the process. Most of my rust at the moment is designed to compile to wasm - and then I'm leaning on nodejs for networking and IO. Writing async networked code is oh so much easier to reason about in javascript. When GAT, TAIT and some other language features to fix async land I'll muster up the courage to make another attempt. But rust's progress at fixing these problems feels painfully slow.

    https://crates.io/crates/smallvec / https://crates.io/crates/smartstring

  • rust

    Empowering everyone to build reliable and efficient software.

    > Rust even seems to provide you with the multi-page compile errors.

    rustc strives to provide you as much relevant context as possible. When hitting very verbose output, it is a hint that what you're trying to do is difficult and will require considered design that the compiler can't help you with. The compiler is trying to help you clarify your code so that it can understand it.

    Looking at the diagnostics shown in the blogpost, I see only one that is indeed terrible[1], but those in particularly are getting less and less verbose as we tackle them one by one with more targeted diagnostics. I also see the lifetime errors (the closure one when you specify the argument type and the on "`Execute` impl is not general enough"), which I wish gave more context.

    As support for HRTBs lands, related errors will stop happening as much and the code will indeed work (or the compiler will be able to tell you what alternative syntax you should be using).

    And, in a general plea for people writing Rust, when you encounter a subpar diagnostic, file a ticket[2].

    [1]: https://hirrolot.github.io/media/rust-is-hard-or-the-misery-... Be aware that this is not exactly what rustc outputs, as some text decoration have been stripped, but that is a lot of text.

    [2]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Ao...

  • teloxide

    🤖 An elegant Telegram bots framework for Rust

    I do completely agree with your advice of using `Arc`s. But unfortunately, sometimes it gets very tedious to deal with all these `Arc`s: https://github.com/teloxide/teloxide/blob/ec1d41220c51872cf9....

    It also blurs the advantage of Rust as a lifetime validation tool if you use reference counting anyways. But it seems that it's the only viable approach for async at the moment.

  • ZIO

    ZIO — A type-safe, composable library for async and concurrent programming in Scala

    Scala. With the functional effect systems, like Cats Effect or ZIO, you get superpowers.

    Not only can write programs that are "async", but you also get easy retries (and other tricks), safe refactorability (because of its Pure FP nature), reliable and painless resource management and some other goodies like STM (Software transactional memory).

    It's really that good.

    https://www.youtube.com/watch?v=qgfCmQ-2tW0

    https://zio.dev/

    https://typelevel.org/cats-effect/

  • SonarLint

    Clean code begins in your IDE with SonarLint. Up your coding game and discover issues early. SonarLint is a free plugin that helps you find & fix bugs and security issues from the moment you start writing code. Install from your favorite IDE marketplace today.

  • cats-effect

    The pure asynchronous runtime for Scala

    Scala. With the functional effect systems, like Cats Effect or ZIO, you get superpowers.

    Not only can write programs that are "async", but you also get easy retries (and other tricks), safe refactorability (because of its Pure FP nature), reliable and painless resource management and some other goodies like STM (Software transactional memory).

    It's really that good.

    https://www.youtube.com/watch?v=qgfCmQ-2tW0

    https://zio.dev/

    https://typelevel.org/cats-effect/

  • jakt

    The Jakt Programming Language

  • ionide-vscode-fsharp

    VS Code plugin for F# development

    F# doesn't have a hard dependency on vscode. Resources from MS will obviously encourage using MS tooling, but ionide [1] is really good. The lsp+neovim workflow is not as good but getting better.

    [1] https://ionide.io/

  • Ionide-vim

    F# Vim plugin based on FsAutoComplete and LSP protocol

  • emacs-fsharp-mode

    F# Emacs mode

  • exhaustive

    Check exhaustiveness of switch statements of enum-like constants in Go source code.

    >> the main thing missing from Go is ADT's. After using these in Rust and Swift, a programming language doesn't really feel complete without them

    What are the differences between an ADT (plus pattern matching i’d reckon?) in Rust/Swift vs the equiv in Go (tagged interfaces + switch statement)?

    One has exhaustive matching at compile time, the other has a default clause (non exhaustive matching), although there’s an important nub here with respect to developer experience; it would be idiomatic in Go to use static analysis tooling (e.g. Rob Pike is on record saying that various checks - inc this one - don’t belong in the compiler and should live in go vet). I’ve been playing with Go in a side project and using golint-ci which invokes https://github.com/nishanths/exhaustive - net result, in both go and rust, i get a red line of text annotated at the switch in vscode if i miss a case.

    Taking a step back, there isn’t a problem you can solve with one that you can’t solve with the other, or is there?

    To take a step further back, why incomplete?

  • mun

    Source code for the Mun language and runtime.

    Have you heard of https://mun-lang.org/ ?

    It's an embeddable scripting language with the goal of being a Rust-like language that supports hot reloading of functions AND data. To achieve the latter, it uses GC'ed memory such that memory can easily be mapped when the memory's type changes.

    It's still in early development but maybe one day will serve your needs :)

NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts