Blog post: Rust vs Julia in scientific computing

Thanks for the post, @Mo8it

There has been some discussion on the post on the Julia Slack. I’ll summarize my view here. As a research software engineer who likes both Julia and Rust, I’m very interested in the topic (I also shared your post on Hacker News). Unsurprisingly, I reach a very different conclusion as you - I think Julia is far superior to Rust for science!

Let me first say that I agree with most of the concrete points you raise in the post. In particular, I agree with the following points:

  • Rust provides more and better static guarantees than Julia, especially guarantees about concurrent programming such as multithreading, and these guarantees prevent some bugs in real life.
  • Rust’s error handling is more robust in the sense that it leads programmers to write code with emphasis on the edge cases, again producing more robust code.
  • Rust’s model of traits and interfaces is far superior to Julia’s abstract inheritance (although I believe Julia could not and should not copy Rust here - Rust’s trait model would not be practical or desirable in Julia). Specifically, Julia’s lack of discoverable and enforcable interfaces is grating.
  • Rust tends to be faster than Julia because of an increased emphasis on zero-cost abstraction.
  • Some of Rust’s tooling, (rust-analyzer, clippy, autogenerated docs) is far superior to Julia’s

I would also add more point in favor of Rust: It’s way easier to just download and execute a Rust program than it is to run a Julia program, because right now, there is no concept of an “app” or “executable package” in Julia, so you need to be fairly familiar with Julia to even run a Julia program (e.g. make a virtual environment yourself, install pacakge into that env, then make a script that calls into the package, then run the script in the right environment with --startup-file=no etc. etc).

However, I disagree with some major points in your post, and I think there are other important points that you have omitted when comparing Rust and Julia:

Most importantly, I think you wildly understate the importance of programmer productivity in scientific programming. It’s not a coincidence that Python (and previously, Perl) are dominant in the field and not C++ depite all the static guantees C++ can provide. For scientific programming in particular, the ratio of code to available development hours is high, so it’s critical to have a language that allows you to write code quickly, and Rust simply does not do that. The language is slow and boilerplate-heavy to write.

Second, scientific computing needs a language where it’s fast to iterate on an idea and quickly pivot, changing an exiting program on the fly. Julia excels at this, whereas Rust is particularly bad at it. In practise, even subtle changes to a type or to the ownership model in Rust tends to cascade through the entire program, requiring you to rewrite a large chunk of your code for trivial borrowchecker reasons. More generally, while the borrowchecker does prevent a whole class of bugs, in like 90% of cases, it just gets in your way pointlessly by preventing you from writing correct code the borrowchecker simply can’t reason about, and which would be solved much easier and more elegantly with a garbage collector. Garbage collection does have some downsides - for scientific programming in particular that it can be slow in certain situations and it can hog memory - but in most cases, it simply solves the problem of memory safety in a single, elegant, fell swoop.

Third, I think you misunderstand what we mean by the “two language problem”. What we don’t mean is that Julia can take the place of every other language because it’s good at everything such that no other language is ever needed. What we DO mean is that Julia is both fast, convenient and flexible enough that you don’t need to prototype and iterate in one language, then implement the final solution in another language due to speed. Which, practically speaking, is what tends to happen. At least in my experience, people don’t rewrite Python to C++ because they want the static guarantees - they do it because Python is too slow.

Then there are a few smaller points

  • Rust doesn’t actually prevent race conditions. It prevents some of them, sometimes (i.e. you can’t have two threads mutating the same variable, but you can absolutely have deadlocks). In general, I find that Rustaceans tend to argue in absolute terms, saying stuff like “Rust is correct”, or “Rust prevents data races”. The true but nuanced “Rust is somewhat safer, and prevents some data races”, while more accurate, is just less sexy. Speaking of which - sure, Rust is faster than Julia because it places emphasis on zero-cost abstractions, but Zig is faster than Rust because it places even more emphasis. So, should you use Zig? Probably not - because both Julia and Rust is fast enough for nearly all practical purposes.
    EDIT: As you mention below, your blog post is careful to mention that Rust only prevents “data races”, which are indeed prevented in Rust, not race conditions in general. You are completely right.
  • In your JET example, you don’t use it the way that is recommended in its documentation and its README page: You need to check for dynamic dispatch with @report_opt before you use @report_call. If you use JET correctly, it will indeed warn against the problem in your example.
  • Interactivity is not just nice when doing stuff like plotting. It’s also useful when programming in general. For example, it’s way faster in Julia to try out different implementations of a single function, or test a single function in the REPL, or to to benchmark or iterate on a small part of the program, because you can do it interactively. The number of times I’ve wished I had a REPL when debugging a Rust program…
  • You don’t mention that Julia tends to encourage code reuse, genericness and code sharing much more than Rust. I believe this is pretty important in scientific code. It’s way easier for me to break open someone else’s Julia code and reuse it than for Rust, because Rust programs tends to be tangled together more. Which again, is really useful for scientific code in general because we ought to reuse each other’s work more.

So in conclusion, while I agree with many of the concrete points, I myself believe that 1) you understate how Rust, by being extremely pedantic, makes writing code slow and ossifies the development process, while 2) also understating the advantages of Julia’s dynamicness and its static capabilities.

Rust is probably superior for writing fundamental command-line tools where you can afford to spend a lot of development time up front, and you really need the guarantee Rust provides, e.g. programs like minimap or samtools, but Julia is superior for most scientific programming.

Of course I hope that Julia’s static guarantees improves, and the many, many correctness footguns in Julia will be reduced over time :slight_smile:

88 Likes