Rust being closer to the hardware should require more code, and effort, to accomplish the same task.
Rust being closer to the hardware should require more code, and effort, to accomplish the same task.
Rust has zero-cost abstractions that feel like using Ruby and a rich type system that makes it incredibly expressive. Working in other languages feels like going back to assembly.
I wouldn't want to design state machines in any other language. Rust's enums make it feel smooth as butter. They're a killer app. (The whole ecosystem is. Cargo. Package naming. Traits. So much good stuff.)
Rust is my most productive language now, and I work in Python, Java, and Typescript codebases frequently.
Edit: I'm being downvoted for expressing preference? What's with all the Rust hate? Learn it instead of being a hater. It's a wonderful tool, and it's silly to dismiss because you think people are being hipsters or something. There's a reason people love it.
It's funny you mention enums, there was just another thread last week where I brought up how crazy it is that sum types (Rust enums) and pattern matching have been around since the 70s but have largely been limited to the FP languages. After extensively using Rust for ~3 years now, I don't ever want to use a language without sum types - you can write incredibly expressive and concise code with them.
The other major productivity boost for me is the ability to compose and chain Iterators and mix those with collection types.
You can basically avoid interface{} in generic Go code with some copy and paste. But you can't really avoid it when you're trying to write code that would be cleaned up by an actual sum type.
Not to commit the predicable sin of comparing Rust to Go any time either is brought up. I just mean to +1 the idea that sum types should actually be a fundamental tool that we can reach for in every language. So weird that it's taking so long to go mainstream, so to speak.
Where Rust diverges is when you're doing async or multithreaded things with complicate lifetimes.
Getting things right from the start instead of solving them JIT as runtime issues pop up in production over the course of the year is Rust's wheelhouse. But even after using Rust for three years, I sometimes feel like I'm one requirement-change away from a problem I can't solve myself in Rust where I could solve it in a couple hours in another language.
Rust does have these sorts of issues wrt. managing highly generic graph-like data, possibly with cycles. That's where tracing GC actually shines, and where writing that whole portion separately in something like Go might be the right answer.
When I'm writing Go, I spend the whole time yearning for the abstractions of Rust, becoming a little more bitter each time I copy and paste or have to manually reify interface{}, knowing my ceremonious verbose Go code is slower than the simpler compact Rust solution.
When I'm writing Rust, I'm loving life until I hit this sort of "intractable" [for me] problem, and I wonder if I should have used Go for this part.
(GDScript is pretty much python)
This isn't a criticism of Rust; it's specifically designed for applications where those things do matter. But for the overwhelming majority of apps they don't, and I'd reach for a different tool.
I've found that once I get into a rhythm and I've been working on something in Rust for a bit, it doesn't feel that hard to deal with all that. And a few times I've thought to myself "hey maybe the GC isn't actually buying you all that much?" But then I go back to a GC'd language and watch just how much faster stuff gets done. It's not even close. I think it's one of these things where your brain doesn't notice the time that goes by when you're doing what is essentially mindless busywork.
Part of this is also having come from doing a fair bit of stuff in Haskell, Elm, and a bit of OCaml; the best "high-level" language features are inspired by that language family (including enums) and so it feels like a better control for the difference a GC makes vs. js and friends. It makes a big difference.
* Better handling of "null"-ness
* Sum types
* Stricter/different error-handling
* Move semantics, which can actually be nice for some APIs outside of any performance considerations
Kotlin checks a couple of these boxes, but then is also GC'd, so also gets rid of a lot of "noise" that would be in the equivalent Rust code.
For typical backend junk, I'd be Kotlin first, but I'd definitely consider Rust if performance (non-IO) was a concern.
There would also be a compiler attribute #![no_gc] that make it so that you have to provide your own implementation of the GC runtime to use GC types. (similar to how executors work for futures)
Rust gives you the features to auto-manage these "silly little details", you simply have to opt-in to them with a bit of boilerplate. Stuff like Rc<RefCell<T>> and the like is there for a reason.
Also "stuff like" is kind of the problem; all of the smart pointer types have differences that matter, and none of them is general enough to cover all use cases. Arc<Mutex<Box<T>>> comes the closest to a "general" solution, but the boilerplate is rather a lot just for the sake of not having to think about this stuff, and you still can't use it if T isn't Send.
You're really picking a fight with the language if you insist on avoiding making these decisions, and in the end it will just slow you down even more than going with the grain. And to add insult to injury, if you write all your code like that it'll likely be slower than OCaml or Haskell anyway. Rust can't keep up with a good GC on allocation throughput; the performance advantages come from managing things yourself (and avoiding heavy allocation in the first place).
Rc and friends are useful, but they don't make the problem go away.
As I said, it's not that it's even really all that hard. But it is time consuming.
Rust is nowhere near the productivity of Ruby or Python. If that's the case for you maybe your projects have some peculiarities or it's a personal thing.
> Working in other languages feels like going back to assembly
This is not a preference, it is an exaggeration and an attack on all other languages.
It is also quite ironic given Rust is intended for low level programming.
With Rust and WASM you pay an expense of FFI between the languages. There are tools that minimize this, but there’s still a cost. Transferring data is limited to very primitive data types today, which adds a cost to translation between Rust and JS. I expect this to reduce in cost as WASM gains abilities to access the DOM and such, but it is overhead. This generally means that for many things Rust is not much faster than JS, but it is for very hot loops over CPU bound computations.
As to productivity. This debate between languages will never end. JS like Python and other interpreted languages give the impression that tasks are being accomplished, but until all code paths are tested, knowing if it is correct is not obvious.
Rust like many other typesafe languages (and it’s definitely on the further end of type safe) allow the compiler to detect errors in usage at compile time, well before testing and production usage. Some of us consider this to be more “productive” as it reduces the overall maintenance of the program after it’s released, but YMMV.
Languages compiled to wasm have an FFI cost, and we will never fully remove it because it just uses different types than Web APIs do. This isn't a Rust problem or a C problem, it's just how wasm is.
Wasm also can't use the JS standard library, and usually ends up shipping some runtime support, like malloc or string handling, which increases download size.
Both of those limitations are why wasm won't make sense for the great majority of web dev work. But wasm shines for "engine" type code, like in this post - pure computation, without lots of links to the outside JS/DOM world.
OCaml is great for the application level stuff, and also performs quite well in general. But for building fundamental parts of the stack in which perf is important (like the multi-threading runtime, the garbage collector, etc.) you need to drop down to C++, C, or Rust.
OCaml is definitely much nicer to use than C and C++. But I don't find it that much nicer to use than Rust TBH.
I find OCaml quite a bit nicer to use than Rust, though in a lot of ways that's due to my preference for structuring code using modules and functors.
However, due to Cargo and the fact that Rust has good Windows support, I end up reaching for Rust much more often than I do for OCaml.
If you’re trying to right very fast, very correct code in JavaScript/TypeScript then Rust may be more productive.
Asking out of ignorance!
- It's low level like C and C++ so it maps cleanly onto WASM
- In your average Rust project, all dependencies are already built from source, greatly increasing the likelihood all your dependencies can be built for WASM
- Already using LLVM as the compiler backend made WASM targeting a lot less work than for languages that need to do that work from scratch
- Probably most importantly, a lot of developers involved in core rustc development were motivated to get Rust working on WASM
At least that's my perception for how Rust became such a prominent language for WASM development early on
The solution other languages targeting wasm typically use is bundling their own garbage collector in the compiled code, which of course adds a bit of code bloat. E.g. C# blazor wasm applications are not exactly small for this reason.
There was a message yesterday in the Kotlin slack about them starting work on a wasm compiler backend for Kotlin (they already have java, native, and js compilers). https://github.com/JetBrains/kotlin/tree/master/compiler/ir/... Interestingly they plan to depend on the wasm GC proposal instead of bundling their own: https://github.com/WebAssembly/gc/
So, things are improving on this front. But it's a big reason why Rust is particularly popular for wasm right now because they have no GC and lots of developer tools that are relatively mature because they've been working on this for a while.
IMHO, this will take another few years to fully mature but inevitably lots of people are going to be writing web applications that don't involve any or very little javascript. Kotlin is starting to look very solid for any kind of cross platform Android, IOS, and web based development (as well as server development, which is what I do). Swift would be another candidate and there already is a wasm compiler for that as well.
That is only the case for open source code like crates.io, there is nothing that guarantees you will get the source code of a third-party, though.
I mention this because in the native world it is common to give customers precompiled libraries.
> - Already using LLVM as the compiler backend
Some people don't seem to know this, but all languages that target LLVM (including C, C++, Fortran, Ada, Julia, Swift and others) can be used in WebAssembly.
Having LLVM doesn't mean that webassembly Just Works, in the same way that having LLVM doesn't mean that all of its architectures Just Work. And even after getting past the "hello world" stage, there's a lot of other work to do to make it more than just a toy.
Let's take Ada, for example. https://blog.adacore.com/use-of-gnat-llvm-to-translate-ada-a... talks about how to use Ada to build stuff for wasm, but you need to include https://github.com/godunko/adawebpack/ to make things work well. Someone had to write that code.
If you just want to run some computational code (which is the case for most of the Wasm use today), it will Just Work, as you say.
In fact, that is how I sped up a webpage: I just wrote myself the minimal support needed to run the code that computed X, and that's it. I don't want the entire world or standard library for computational bits to work.
This is just true of any architecture. LLVM is a toolkit, it isn't magic.
“Today there are several languages in the ML family; the three most prominent are Standard ML (SML), OCaml and F#. Ideas from ML have influenced numerous other languages, like Haskell, Cyclone, Nemerle, ATS,[citation needed] and Elm.[3]”
If you know F#, OCAML, Haskell and scala you can see that the first two have an extremely similar syntax to ML while for the last two the syntax is very different.
With the patterns and libraries used in just this example, you can write a very high performance, memory efficient web server that can handle a large volume of concurrent requests that involve database interactions: https://github.com/actix/examples/tree/master/async_pg
All of the work has been consolidated down to a single file for illustration purposes, and it would usually be spread across files. Does it seem like a ridiculous amount of additional work, compared to other languages? I am misleading you some as there is actually a lot more to write the moment we move beyond plain vanilla workflows but this example proves what is possible.