I have no idea why this blog is making the rounds again and I've learned a lot of Rust since, see my (badly benchmarked!) presentation at this year's Scientific Computing in Rust 2024:
I'll have to revisit this blog at some point and see what I should update, as well as a "From Rust to Julia" post for those that want to venture into that world as well.
Peeve of mine: blog posts that lack a date stamp. The only date appearing anywhere in the article is at the bottom, where it says "Last modified: August 29, 2024." So for all I knew it was a brand new post, until this comment. How old is it actually?
I'm a physicist whose written substantial projects in both Julia and Rust (and C/C++/Object Pascal/Java/Python/...). I'm also a person who refactors frequently. While my mind was initially blown away by all the cleverness in Julia, I eventually found that for non-trivial packages the "time-to-first-X" became painful - way more painful than compiling Rust code (mostly with debug occasionally with --release). Plus the amazing generality of Julia code was both a blessing and a curse. Julia really needs interfaces to ensure that a duck really is a duck. In the end, I find Rust (despite/maybe because of) the picky compiler to be overall much more productive. With Rust, I find I can make major refactors, when the code compiles and my test code runs, I'm good to go. With Julia, months after I've refactored, I'm still finding footguns. I still use both but it is Rust that has my heart these days.
Other thoughts: Developing Rust on Windows is painful but on Linux a joy. both cargo and Julia package managers are wonderful compared to other language alternatives.
I'll agree on several counts (as a physicist too!):
* Rust's tooling lets you have a base-floor of what is acceptable code that is much more palatable than non-top tier Julia codes. There's a world of difference opening a random crate's code in Rust that has been linted/clippy'd/check'd by the compiler rather than a random Julia package that a coworker/colleague just fired up. This tooling is getting better and better in Julia and I'd be interested to hear which Julia projects you worked on were really hampered by TTFX and refactoring woes. Hard agree on the footguns - I want the VSCode plugin to be better at detecting dead code, it cost me weeks on a project lately.
* I'm also excited for some notion of interfaces to come to Julia. Perhaps it will be a 2.0 thing, but there's still lots of design stuff to figure out.
* We have different understandings of "productive" for different focii then - I still find it painful to do the equivalent of `rand(1:10, (20, 20))` in Rust and I can get STEM people to be productive with Julia before you can even finishing saying "borrowchecker".
The nicest way to avoid the "oh a lot of boilerplate!"-problem with implementing "Add" for a custom type is by using the "derive-more"-crate. Then you simply put a "#[derive(Add)" above your struct. That was the initial reason I switched to Rust myself: because I was writing a dynamical systems solver in C++ and had to overload arithmetic operators for my state-struct. But doing this was so complicated and I never felt sure of it performance wise (what if one side is a reference? And so on).
In rust I have a macro and don't have to ever worry about that again.
That's not really the case. For loops are certainly less common compared to C, but they are completely fine to use. Forcing every loop into an iterator-chain style can sometimes be more confusing.
Not to mention doing for-iterations with early exits. Yes, you can use try_fold, but if I'm not writing generic iterator library code, I'd prefer explicit for in cases like this.
> writing Fortran-ey code instead of iterators will lead to pain and slower loops.
No it won't. At least not in the general case. Not to mention that iterators also have performance footguns that are pretty easy to trigger.
>That's not really the case. For loops are certainly less common compared to C, but they are completely fine to use. Forcing every loop into an iterator-chain style can quickly become an unreadable mess.
There is the "for" expression, but that does not give you a C-style "for" loop. It gives you something akin to a "foreach" you might see in Java, C#, Go, etc.
I think that's bit too pedantic. Most people in the Rust community refer to for expressions as for loops (including the book).
It's especially odd to point out this language detail, because for expressions always return the unit type. So it's not like you can do something with the return value (unlike loop expression, which can evaluate to one of its break expressions).
Sure, but that's kind of beside the point I was making.
The author was contrasting loops - an imperative construct (which both C-style for and iterator-based for are examples of) - to the functional style of chaining iterator functions, adapters & reductions and passing closures around.
FWIW I agree with you, it's just that I was trying to make a distinction between imperative and functional style in Rust (just like the author was trying to do) - not literally claiming Rust has C-style for loops.
> For loops are not idiomatic in Rust - writing Fortran-ey code instead of iterators will lead to pain and slower loops. Spending time reading the examples in the iterator docs and the community solutions in the exercisms will help a lot.
I think they are talking about C style for loops or Fortran style do loops, they explicitly contrast against iterators.
Edit: Although, I wonder, what does an iterator like you’ve shown compile down to? I’d imagine Rust is smart/modern enough and that iterator is common enough that the assembly emitted looks a lot like a c for loop. Or better.
I understood them to be saying that they would prefer
(0..10).iter().for_each(|x| println!("{x}"))
over the thing that I wrote.
Many folks do not think of Rust's for loops as using iterators under the hood, and so will often talk about them as though they don't use iterators, even though they technically do.
When I wrote this, my main takeaways were numerical computations with arrays, which don't tend to use for loops in Rust, and which require grokking iterators for maximum performance and rayon-parallelism boons.
To a Julia audience, seeing this style of iterator chaining is quite a new flavor to think about composing computation, so I wanted it was with that audience in mind that I wrote this.
Rust only "really" has one loop, the one named loop. Loop is an infinite loop that you can break out of (using the break keyword of course).
All the other syntax, including both "for" and "while" loops and the related but technically different "while let" loop, while useful to humans to communicate intent, aren't semantically different - and so they're transformed early in compilation, they are just "syntax sugar" and are de-sugared into rather elaborate loops with their exit condition now a loop break condition etc.
But yes, as in C++ and similar languages, the iterators generate the same machine code as you'd have gotten for writing the C-style for loop from the 1970s, it's just that in Rust you literally can't write that loop.
Now I’ve become confused as to what it means for a language to have a thing.
Hypothetically if you do
for i in 1..10 {
println!("{i}");
}
(From earlier) the compiler should have enough information to fully unroll the loop if it feels like it. So it is a different syntax from the programmer’s point of view. Maybe it goes through some “this is a loop” stage inside the compiler. But then hypothetically the assembly output could… end up not even having a loop at all (fully unrolled, no jump back, haha).
What your parent is saying is that the compiler treats that loop as
{
let result = match IntoIterator::into_iter(1..10) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let i = next;
let () = { println!("{i}"); };
},
};
result
}
(95% sure I got that correct)
And so in some sense, "loop" is more primitive than "for".
> But then hypothetically the assembly output could… end up not even having a loop at all
Absolutely, with any of these loops, the compiler may not emit a loop in assembly. And in this case, that seems to be true: https://godbolt.org/z/7Gq69Gvre
>>That's not really the case. For loops are certainly less common compared to C, but they are completely fine to use. Forcing every loop into an iterator-chain style can quickly become an unreadable mess.
Implies a C-style for loop, some sort of counting accumulator, and an update expression. That doesn't exist in rust. That's all I was saying.
I know who you are, so I know I'm preaching to the preacher.
Really enjoyed this article, even though I’ve only ever explored the basics of Julia. I’m always on the lookout for articles and guides that would help people with basic R, Julia and Python REPL skills to learn software engineering patterns, computer science basics or, especially in the case of R, more general purpose languages.
interesting question: does it still make sense to have different or specialized languages for scientific computing vs generalized software engineering?
It makes a lot of sense in fields where there are benefits to using code (instead of GUIs) to generate statistics and figures, as long as learning the basic syntax of the language to write this code can be done in a week or two.
Learning just enough R (or Julia or Python) for the purposes of making replicable, easily updated, easily styled, easily adjusted tables and figures for research is something that a freshman in social sciences and humanities can handle. And for many academics that level of knowledge is enough for grad school and eventually even tenure.
That level of knowledge will not necessarily include even the most rudimentary software engineering skills: the code will (most likely) live in a single long file. That file will contain both working chunks of code, old/broken/redundant chunks of code as well as comments and notes both related and unrelated to the task at hand. It will never be executable in a linear fashion (only chunks at a time in a REPL). And some data importing or package/library management will still have to be done through a GUI.
you make an important point -- there's the choice and mechanics of a computer language (which is where i was thinking), and then the overall hci of the data analysis activity. should it look like software development or use of an (incredibly powerful) interactive calculator with dsls that are carefully designed to match the lingua franca of the fields they serve. (or, assuming possible, should the lingua franca move to a dsl and hci paradigm that is easier for humans and machines to reason about with minimized ambiguity?)
I'm still not understanding the switch to Rust other than for education (which is fine). All of the things you get from Rust with memory safety you get in Julia with the Garbage Collector?
At least for some scientific computing codes, but I did learn a lot of Rust along the way and extended my knowledge into (some) embedded and systems programming. Learning has been fun and the Rust people have been very friendly.
I think there is one thing Rust can help you with that Julia doesn't which is race conditions. In Rust they're compiler errors, in Julia they just... happen?
Interesting post, even though I don’t understand “Why Rust” because all the reasons cited are not an issue with Julia (eg memory safety).
This seems to not be about Julia at all. It just a post of a person who only knew Julia, and is now wanting to learn another language (which happens to be Rust)
Note that Julia does allow memory unsafety, for example you can mark array accesses with `@inbounds` to remove bound checks, kinda like how you can use `unsafe` in Rust except it looks much less scary.
It also doesn't help that the official example for how to use it safe was actually not safe [1]. Granted, this is just a single example and has been fixed since then, but it doesn't give a nice impression of their mindset when dealing with memory safety.
More in general there doesn't seem to be a strong mindset for correctness either. See [2] for a collection of such issues.
I don’t think it’s a case for switching from one to the other. It seems like an intro to rust from someone whose first language was Julia (which is used as the perspective to contrast with).
There is a good list of Rust learning resources though…
I’ll agree that it’s mostly about its like using Rust compared to Julia. It’s not very dismissive of Julia. The first paragraph lets you know what your in for.
That stood out to me as well. The collection of reasons given to prefer Rust to C happens to be completely compatible with preferring Julia to C as well.
There's a different collection of reasons which wouldn't have that property (smol binary, suitable for realtime and embedded, etc), but it isn't the ones the author picked.
Precisely, the most natural symbiosis for a Julia + Rust combo seems to fill a hard-realtime, critical safety or embedded niche with offline simulation.
The Py03 and maturin story is something that we haven't brought to Julia yet and I'd love to have some time to work on a tighter integration story there. For example, getting a hot-reload with Revise.jl and a bindings generator that watches file changes and recompiles and the fly so that a Julia REPL gets updated with the new definitions would be so enviable.
I haven't spoken to Tim Holy to see how to string that together but many of the pieces are there.
This is not about switching. He mentions his first language was Julia and he became interested in learning Rust. He answers that question and there is nothing wrong with someone getting interested in something by watching talks and getting inspired by people's opinions.
I don't think this is an article about switching. The author still uses Julia --- he has just learned Rust as well! This is an article about the learning experience and some notable differences and takeaways from the adventure into Rust.
Add new builtins to Bash using a shared library (or .bundle on macOS) written in Rust. Bash also allows running bash other commands from native code. (There's a rust crate for creating bash builtins, but it doesn't currently compile on mac due to some improper and some missing linker flags.)
I have no idea why this blog is making the rounds again and I've learned a lot of Rust since, see my (badly benchmarked!) presentation at this year's Scientific Computing in Rust 2024:
https://www.youtube.com/watch?v=bdsBhqKOe7A
and a PR I sent in for the Rust compiler to speed it up for some low hanging fruit:
https://github.com/rust-lang/rust/pull/110477
I'll have to revisit this blog at some point and see what I should update, as well as a "From Rust to Julia" post for those that want to venture into that world as well.
reply