Ask HN: Should I learn Rust or Go?
I am a full stack TypeScript dev looking to broaden my skill set by learning a new language. I’ve been weighing Rust vs Go for a long time but can’t really decide which would be a better use of my time. I often hear that people who try one of these languages after coming from something like TypeScript feel that they really level up their abilities/understanding.
I’d like to learn whichever language will make me a “better” coder in the long term. My understanding is that Rust would be the way to go here. However, I see that the Rust learning curve is significantly higher than Go’s, and that something like setting up a server in rust will take much longer than Go. It sounds like Go is better for “getting things done” but I’m wondering if I’m leaving growth/understanding on the table by going from one garbage collected language to another.
I am interested in hearing anyone’s thoughts on this matter. Especially if you have experience going from js/ts to one of these.
Thanks and happy 4th
126 comments
[ 2.7 ms ] story [ 197 ms ] threadhttps://go.dev/ref/spec https://doc.rust-lang.org/book/
https://go.dev/learn/
0: https://doc.rust-lang.org/reference
Speaking as someone who is really enjoying learning Rust, time taken to read the book isn't a good metric for understanding the language.
I might have agreed with this before I tried using Rust's strings, borrow checker and lifetimes in anger. I had some real speed bumps with these even given my prior knowledge of C#, Common Lisp and C.
You will feel productive early on in Go, but it plateaus. Rust has a lot more CS concepts and I think will be the more fruitful learning experience.
Also, Rust can lead you to WASM, which might work well with your web background.
Please correct me if I am wrong.
EDIT: s/runtime/compile-time
If the ownership rules determine at compile time that your memory assignments are fine (no double free, etc.), you can avoid using reference counting (and the associated GC).
Reference counting is a form of GC, but in itself it is not sufficient (it will leak circular references as those will always reference each other). Due to its simplicity, in a sufficiently expressive language one can create this abstraction (see shared_ptr in c++, RC, ARC in Rust) and with the necessary caveats of not using circular structures with it, you get an ergonomic semi-GC without runtime support.
Compile time/runtime issue is slightly different - in case of many allocations one can know where you will have to free it even at compile time. C++ has a concept called RAII, which is basically “put a free at the end of the scope”. This works surprisingly well for most use cases and Rust sort of mandates this semantics by the compiler in the general case.
But an object’s lifetime can’t be known in advance (think of a User object for a logged in user. Is it still needed or had it expired?) in the general case, that’s why runtime GC is needed like refcounting or tracing GC in managed languages.
There's no disputing that v8 has come a long way, so likely it's not going to be that much of a perf gain, more interesting to me is bringing more advanced languages (such as Rust) into play, great and all as TypeScript is.
Elixir shines at concurrency so it will feel more like nodejs and similarly runs on a VM like Node or Java.
Elixir, like Rust, will help with learning functional programming paradigms more than Go.
It's true that as a beginner you can simply do web development with Phoenix and that feels similar to Rails (Or Django / Laravel / Spring / whatever web framework ). But OTP requires a different way of approaching problems.
Elixir is the new Erlang. Ruby is dead (I used to <3 ruby). Go is the new python, zig is the new c, and rust is the new c++.
It's not that it's functional (that's completely secondary in Erlang) but its fault tolerance primitives (processes, pids, mailboxes, monitors and links) and the OTP abstractions (GenServers, Supervisors, etc).
You learn to think in terms of distributed systems and think about fault tolerance in a way you cannot in other languages as they don't provide the primitives. It's quite a big leap and it takes time to sink in.
I'll also add that Haskell, Clojure, and other functional languages will help you think in an entirely new way to program. Python or Ruby are also less mind-expanding, but was a nice change of pace for me at least from my C# day job.
Learning window functions and more advanced bits of SQL that an ORM doesn't give you for free can also be a fun time, along with getting deeper into what indexes can really do.
If you want to understand internals of programming languages then Go isn’t a great choice because it abstracts away so much stuff.
C is still fairly high-level.
At this rate, Python will be called "low level" 50 years from now.
But to the OP: Rust is just "C for the 21st century" (despite people often calling Go that). The UX is stellar -- `cargo run` can't be beat, integrated unit testing, safety concepts learned in the last few decades are applied by default, and the docs are great[0]. I don't think there's any reason to learn C first, especially if you're coming from a "high level" background like TypeScript, other than experiencing culture shock and legacy braindeath horror.
0: https://doc.rust-lang.org/stable/book/title-page.html
But for gaining understanding, I think because C is so 'raw', I think that makes it useful for developing further understanding as a programmer.
A quick dive into C is a good idea for learning. It is hugely influential and basically the Latin of programming languages so any knowledge pays huge dividends down the road even if you never use it directly.
Whether you should use C was not the question. There are valid uses though even for new projects and if just for the few exotic targets that Rust doesn't yet compile to.
Like you said, for "getting things done" that is not low-level, systems, or game programming, Go is the more reasonable choice. It's more straightforward, faster to iterate in, usually more concise, has easy concurrency primitives, and is more widely adopted in the industry.
TLDR; I'd go for Rust if you want to optimize for learning and Go otherwise.
I would definitely pick Rust as it is more different than JS.
Yes, there is a higher learning curve. But it will broaden your perception on programming languages.
As an added bonus, rust is very popular to create WASM stuff. So you can apply it to the JS world.
Happy 4th
Rust has a steep learning curve, like you said, which is one reason why it won’t be nearly as common as Go in the workplace.
Rust I like it but worry it hasn't really taken off as I expected. C++ seems resurgent. The C++ world is vast, perhaps you'll learn more with that and the skills will be more useful. Either C++ or Rust dont get used in full stack though so it really is a different world.
Now if you plan to do number crunching, low level algorithm or scientific computing, Rust by all means. Unless you glue algos, then Python or Julia
Tangential & important/useful to this question is:
"Ask HN: Great visual or other breakdowns of PL's & their categories/sub-c's?"
https://news.ycombinator.com/item?id=31919255
Posted over 5 days ago, sadly, there was zero responses.
Should I try reposting from scratch, or 1st w&s if this bump sparks things?
Ty
I personally find that even spending a little bit of time in a programming language with a different paradigm is incredibly helpful. So I'd suggest that Go is probably too similar to be maximally helpful, Rust is really good, but something like Haskell might give your brain a better workout even if you never actually use it for anything concrete.
If your priority is to get things done, do them in TypeScript unless you've a really good reason to learn another ecosystem at the same time. If your priority is to learn and get better at programming in general, digging into pretty much anything else will help. Just don't necessarily expect to get anything done.
Go - concurrency with channels (that's a big one, gives you a good tool to think about concurrency even outside Go), appreciation of language minimalism
Rust - what it takes to have memory safety without garbage collector
Speaking of mental tools for dealing with concurrency I recommend looking at Clojure with its concurrency based on software transactional memory and immutability.
Source: myself and literally every other person that I’ve talked to about the matter.
They each present trade offs that make them a better tool under particular circumstances. While Rust exposes you to more sophisticated typesystem features, Go's M:N scheduler is an incredible piece of technology that is (IMO) unmatched by any other mainstream language.
Finally, regarding the garbage collector, if you learn both languages, you'll get to viscerally experience the tradeoffs of having the garbage collector (try writing the same program in each language). There are some projects where it makes sense to use one and others where you shouldn't. Trying out both is the best way to build up intuition for this kind of trade off.
It really depends on the goal. If one wants to end up with an elaborate hellow world, probably yes, one can learn both.
Learning Rust to make it applicable to mid/large-scale projects, and potentially for a professional transition (my case), is a one-of-a-kind commitment, in my opinion, hardly compatible with learning another language at the same time.
I think there's some linearity to these tasks. Rust (and C++) are an amalgamation of many complex features that are present in simpler predecessors. Learning C for example will not only speed up your ability to learn Rust, but also leave you with a deeper understanding of the core principles.
It's not that you leave it on the table with Rust/C++ but more that you'll have a deeper understanding for what's going on if you understand how pointers work in C.
To sum my thoughts on this up: can you help me understand why pointers would be useful for me to invest in learning on the level of C versus continuing to let them be abstracted away/not present in my language? What would that knowledge do for me? Will I be a better problem solver etc. thanks again!
It's important to learn how they work, because it'll give you a better idea of what's actually happening in the higher level languages you're using. That may not be important if you stick to JS or Go, but if you'd like to learn Rust then it's impossible to accurately think through tradeoffs like whether to use Box, Rc, Arc, etc. without understanding how pointers work. In other words, I'd only recommend learning Rust if you take the time to understand pointers, and the best way to understand pointers is to write C.
In the other thread you mention features. I suggest C because of its "simplicity", that is, its lack of features. It really is a thin abstraction on top of assembly. C's closeness to the hardware is edifying.
Moreover, since the goal is learning, History is quite relevant. Languages evolve in the context of their predecessors. You probably already know this for typescript since it is so close to JavaScript. But how do C++ and rust relate to C? And how does Go relate to C++ and C?
Languages always involve tradeoffs. For example, a language that checks memory bounds at runtime (Go) uses more CPU cycles than one that doesn't (C). A compiler that does checks is more complex than one that doesn't. Language features can help with safety (rust), but then the language takes more time to learn. Complexity can be hidden (Go garbage collection) to make up front learning easier, but this can make the language less flexible or more difficult to debug. Complexity can be exposed later but then the language learning curve is lengthened.
I'm not suggesting you go become a C pro, but learn enough to shoot yourself in the foot. It will help contextualize the features of these other languages and give your more mental tools for decision making in your own code.
Learning a new language is not something to take lightly. Should I take 6 months in order to get more solid foundations before moving to Rust? 1 year? N years?
My biggest hurdle has been ownership ("borrow checking", which is the common problem) in the broad sense (which includes: learning to design a whole program considering ownership). This is something that C/++ programmers are surely familiar with, so it absolutely helps, but I don't think it's efficient to learn C/++ in order to step up to Rust.
I certainly don't think C++ is a prerequisite to learning Rust, but I do think your path to deeply understanding Rust will accelerate if you understand C (or specifically, understand pointers and heap allocations). But to each their own!
I talked to a C programmer who said thinking in lifetimes (when to free()) was second nature, so didn’t even think heavily about it.
Java’s Loom will change the picture big time, and it is already available in preview mode! But otherwise I agree.
I'm not up to speed with the latest and greatest in Haskell or Elixir, so they very well may have something similar. Rust has great runtimes, like tokio, that do similar things, but without rope stacks and with painful async/await syntax.
Go's scheduler was only somewhat unique in the combination of features it pursued:
1. M:N
2. Stackful (i.e. unoptimized memory usage for each task/goroutine)
3. But using very small stacks[1] so it's easier to create a very large number of goroutines.
4. Integrated with the GC
5. Colorless functions
6. Built-in
7. No access to native threads
8. Not configurable or customizable.
9. Run as Native code, without using a virtual machine.
Colored functions (Async/await or Kotlin's suspend) are a matter of taste. They're heavily criticized for the burden they add, but advocates prefer the extra type-safety they provide. If you want to be able to statistically analyze or data races (or prevent them completely, as Rust does), I don't think you can avoid them.
Speaking of Rust, Rust did start with an M:N, work-stealing scheduler based on stackful coroutines. This scheduler was eventually removed[2] from the standard library, since it was deemed a bad match for a systems language.
Go was originally marketed as a systems language, but it was really a language that was optimized for writing concurrent servers by large teams of programmers with varying experience[3]. Specifically it was designed for server software at Google[4], and to replace the main place C++ was used for in Google: I/O-bound server software. That's why Go made very radical choices:
- Go Native (we still need good performance, but not C++-level) - Maximize concurrency - Make concurrency easy - Use GC (we need the language to be much easier than C++) - Minimize GC pause times (we need reasonable performance at server workloads)
This meant that the Go M:N Scheduler was usually the best performing stackful scheduler for server loads for a while. Interpreted languages like Python, Node and Lua were slower and were either single-threaded or had a GIL. Erlang and Java used a VM, and weren't optimized for low latency GC. C and C++ had coroutines libraries, but since these languages were not garbage-collected, it was harder to optimize the coroutine stack size.
I think it created a wrong impression that the Go scheduler was revolutionary or best-in-class. It never was. The groundbreaking thing that Go did is to optimize the entire language for highly concurrent I/O-bound workloads. That, along with a sane (i.e. non-callback-based) asynchronous model and great PR from being a Google language helped Go popularize asynchronous programming.
But I wouldn't say it is unmatched by any mainstream language nowadays. Java has low-latency GC nowadays and it's working on it's own Go-like coroutine implementation (Project Loom). But all mainstream JVM languages (Kotlin, Scala and Clojure) already have their own M:N schedulers.
Rust, in the meantime, is strictly more performant than Go: It's using state-machine based stackless coroutines, which emulate the way that manual asynchronous implementations (like Nginx) are done in C. You can't get more efficient than that. Not to mention that Rust doesn't have a GC and features more aggressive compiler optimizations.
[1] IIRC, initial stack sizes has changed between 2k-8k during the language lifetime, and the stack resizing mechanism has changed as well.
[2] https://github.com/rust-lang/rfcs/blob/master/text/0230-remo...
[3] https://talks.golang.org/2012/...
I guess it depends how you define mainstream, but in Rust, Rayon has a work stealing scheduler too.
On the other hand, it has existed in Erlang for decades, and Elixir takes full advantage of it.
As for my 2c, I would say Rust is a better choice than Go in terms of the first language to learn. The main reason for this, is it is easy to embed in other languages. When I get to a problem that is too slow in a higher level language, eg. Python or Elixir, Rust is a great way to solve this problem. Just have a look at polars (https://www.pola.rs/).
Do you want a job in the short-term? Choose Go.
Do you want a job in the long-term? Choose Rust.
- choose Go because: - it is easy to learn - it is fun - it is productive - it has good performance - it has good maintainability
Or you can:
- choose Rust if: - you have the time to learn the ins and outs - you have time to keep up with the never ending new features - you have interest in write software that is usually written in C or C++
What you need to have in mind is that both languages have benefits and trade-offs.
Go for example is easy to learn because is a small language but because of that you will need to write more code. Modern developers are usually averse to write lots of code. But write the amount of code that Go requires you to write will make you think better about what you are writing and at the end this will make you a better developer.
Rust takes time to learn and features keep bubbling up from time to time and some times development with Rust can be slow compared to other languages, but it is an excellent replacement for C or C++ if you are writing software for a supported architecture, because you will have the C/C++ performance without lots of bugs related to manual memory management.
Another thing to keep in mind is not to use Go or Rust when you should not use them. For example, you will see a lot of people using Rust for the sake of using Rust just because Rust is "memory safe", but Go is "memory safe" too; the difference is Go is garbage collected and Rust is not.
If you can afford a garbage collector in the software you are writing, don't waste your time with Rust if the software domain is one that Go excels, like networking programming.
But don't use Go for the sake of using Go as many people do, for example, trying to replace C or C++ with Go, because Go is not a replacement for C and C++.
- The community is very helpful on Discord (https://discord.com/invite/rust-lang-community).
- The package ecosystem (crates.io) makes it very easy to find and import things, and they're consistently documented.
- There's a path to compiling on web via WebAssembly (https://rustwasm.github.io/wasm-pack/)
In a few weeks of learning the language, I managed to put together this visualization: https://twitter.com/admiralakk/status/1542560489091350529
But realistically, either Go or Rust will be fine. You should pick the one you're more likely to stick with and learn, even when it gets tough. I've found the community aspect to be very helpful, because then at least I know I'm not the only idiot out there.
> I am a full stack TypeScript dev looking to broaden my skill set by learning a new language. I’ve been weighing Rust vs Go for a long time but can’t really decide which would be a better use of my time. I often hear that people who try one of these languages after coming from something like TypeScript feel that they really level up their abilities/understanding.
I don't know if that's a joke or just misinformation
If you like functional programming, or want to write embedded software, or high-performance software, I'd go with Rust. All those things are a lot easier in Rust.
Personally, I use Go for scripting and Rust for big projects, because Go compiles fast enough, has a nicer stdlib and dependency management than Python; while for big project my natural tendency to think functionally would make it too annoying having to translate simple concepts into programs twice as long as they need to be, expressing what they're doing very indirectly. But I don't write web services, so that's a different story.
Also the argument about the stdlib is kind of weird. It's a deliberate choice to keep the stdlib small, also because adding a new package to the projects takes a few seconds. If you were tasked to do a web backend with a limitation of "you can't use any external libraries" then yeah, I'd say Go might be easier, but otherwise there's almost no difference in practice