2025-05-29 08:00:00
Few developers have been as influential to my career as Niko Matsakis. Of course he is a world-class engineer with a PhD from ETH Zürich, a Rust core maintainer who has been working on the language for way more than a decade, and a Senior Principal Engineer at AWS. But more importantly, he is an empathetic human and an exceptional communicator.
I’ve personally been waiting for one year to get him on the show and steal one hour of his precious time. Now, finally, I got my chance at live recording at Rust Week 2025. The result is everything I hoped for: a trip down memory lane which takes us back to the early days of Rust, an honest and personal look at Rust’s strengths and weaknesses, and a glimpse into the future of the language. All of that packed with insightful anecdotes based on Niko’s decades of experience. If you like Rust, you will enjoy this episode.
CodeCrafters helps you become proficient in Rust by building real-world, production-grade projects. Learn hands-on by creating your own shell, HTTP server, Redis, Kafka, Git, SQLite, or DNS service from scratch.
Start for free today and enjoy 40% off any paid plan by using this link.
Rust is the language which brought us all together. What started as a side-project of Graydon Hoare, a single developer at Mozilla, has grown into a worldwide community of hobbyists, professionals, and companies which all share the same goal: to build better, safer software paired with great ergonomics and performance.
Niko is a long-time Rust core team member, having joined the project in 2012. He was and still is part of the team which designed and implemented Rust’s borrow checker, which is the language’s most important feature. He has been a voice of reason and a guiding light for many of us, including myself. His insightful talks and blog posts have helped countless developers to see the language and its goals in a new light.
Thanks to RustNL, the organizers of Rust Week 2025 for inviting Simon and me to record this episode live. They did a fantastic job organizing the event, and it was an honor to be part of it.
ret
for return
, cont
for continue
~T
and @T
as syntax features instead of Box<T>
and Rc<T>
std::threads
- Not green, just part of the standard librarystd::rc::Rc
- The @T
of std
std::boxed::Box
- The ~T
of std
with some special compiler saucestd::sync::Arc
- Thread safe Rc
pyo3::Py
- A pointer type in a different library!std::marker::Send
- A trait without even a method to dispatch, aptly placed in the marker
modulestd::marker::Sync
- Another example of a marker traitawait x
/ x.await
discussion?match
operator for this?2025-05-21 08:00:00
The Rust standard library, affectionately called std
, is exceptionally well-designed, but that doesn’t mean it’s perfect.
More experienced Rust developers tend to navigate around some of its sharper parts.
In this article, I want to highlight the areas in std
that I personally avoid.
Keep in mind that this list is subjective, so take it with a grain of salt.
My intention is to point out some pitfalls and suggest alternatives where appropriate.
Rust’s threading library is quite solid. That said, managing threads can be a bit of a footgun. In particular, forgetting to join a thread can have some unexpected side effects.
use thread;
use Duration;
;
In the above scenario, cleanup tasks (such as flushing caches or closing files) might not get executed.
So, even if you do nothing with the handle, it is still a best practice to join()
it.
For more details on the topic, check out matklad’s article: “Join Your Threads”.
In fact, there was a proposal to make std::thread::JoinHandle
be #[must_use]
, but it was ultimately declined because it would produce too many warnings.
This comment summarized the situation pretty well:
I’d say the key issue here is that
thread::spawn
is the easiest way of spawning threads, but not the best one for casual use. Manually calling.join().unwrap()
is a chore and easy to forget, which makesthread::spawn
a potential footgun.
For new code, I recommend using thread::scope
instead, which is a much better API in every conceivable way.
The documentation addresses the above issue directly:
Unlike non-scoped threads, scoped threads can borrow non-
'static
data, as the scope guarantees all threads will be joined at the end of the scope. All threads spawned within the scope that haven’t been manually joined will be automatically joined before this function returns.
Alternatively, you could use a thread pool library or rayon, in case you have an iterator you want to parallelize without manually managing threads.
std::collections::LinkedList
Implementing a linked list in Rust is not easy. That’s because Rust’s ownership model is detrimental to self-referential data structures.
Some people might not know that the standard library ships an implementation of a linked list at std::collections::LinkedList
.
In all those years, I never felt the urge to use it.
It might even be the least-used collection type in the standard library overall.
For all ordinary use cases, a Vec
is superior and straightforward to use.
Vectors also have better cache locality and performance characteristics: all items are stored in contiguous memory, which is much better for fast memory access.
On the other side, elements in a linked list can be scattered all over the heap.
If you want to learn more, you can read this paper, which contains some benchmarks.
You might be wondering why linked lists get used at all. They have their place as a very specialized data structure that is only really helpful in some resource-constrained or low-level environments like a kernel. The Linux kernel, for example, uses a lot of linked lists. The reason is that the kernel’s intrusive linked list implementation embeds list nodes directly within data structures which is very memory efficient and allows objects to be in multiple lists simultaneously without additional allocations. 1
As for normal, everyday code, just use a Vec
.
Even the documentation of LinkedList
itself agrees:
NOTE: It is almost always better to use
Vec
orVecDeque
because array-based containers are generally faster, more memory efficient, and make better use of CPU cache.
I believe the LinkedList should not have been included in the standard library in the first place. Even its original author agrees.
There are some surprising gaps in the API; for instance, LinkedList::remove
is still a nightly-only feature2:
use LinkedList;
Even if you wanted a linked list, it probably would not be std::collections::LinkedList
:
std
.
An intrusive list is what the Linux kernel provides
and even Rust for Linux has its own implementation of an intrusive list.There is a longer discussion in the Rust forum.
Better implementations exist that provide more of the missing operations expected from a proper linked list implementation:
There’s a thing to be said about BTreeMap
as well, but I leave it at that. 3
Path
does a decent job of abstracting away the underlying file system.
One thing I always disliked was that Path::join
returns a PathBuf
instead of a Result<PathBuf, Error>
.
I mentioned in my ‘Pitfalls of Safe Rust’ article
that Path::join
joining a relative path with an absolute path results in the absolute path being returned.
use Path;
I think that’s pretty counterintuitive and a potential source of bugs.
On top of that, many programs assume paths are UTF-8 encoded and frequently convert them to str
.
That’s always a fun dance:
use Path;
These path.as_os_str().to_str()
operations must be repeated everywhere.
It makes path manipulation every so slightly annoying.
There are a few more issues with paths in Rust:
Path
/OsStr
lacks common string manipulation methods (like find()
, replace()
, etc.), which makes many common operations on paths quite tediousPath
/OsStr
handling that doesn’t fit the platform well. It’s a cross-platform compromise, but it creates some real problems.Of course, for everyday use, Path
is perfectly okay, but if path handling is a core part of your application, you might want to consider using an external crate instead.
camino
is a good alternative crate, which just assumes that paths are UTF-8 (which, in 2025, is a fair assumption).
This way, operations have much better ergonomics.
In my opinion, it’s actually great to have some basic time functionality right in the standard library.
However, just be aware that std::time::SystemTime
is platform dependent, which causes some headaches.
Same for Instant
, which is a wrapper around the most precise time source on each OS.
Since time is such a thin wrapper around whatever the operating system provides, you can run into some nasty behavior. For example, this does not always result in “1 nanosecond” on Windows:
use ;
The documentation does not specify the clock’s accuracy or how it handles leap seconds, except to note that SystemTime
does not account for them.
If you depend on proper control over time, such as managing leap seconds or cross-platform support, you’re better off using an external crate. For a great overview, see this survey in the Rust forum, titled: ‘The state of time in Rust: leaps and bounds’.
In general, I believe std::time
works well in combination with the rest of the standard library, such as for sleep
:
use thread;
use Duration;
;
sleep
…but apart from that, I don’t use it for much else. If I had to touch any sort of date calculations, I would defer to an external crate
such as chrono
or time
.
As you can see, my list of warts in the Rust standard library is quite short. Given that Rust 1.0 was released more than a decade ago, the standard library has held up really well. That said, I reserve the right to update this article in case I become aware of additional sharp edges in the future.
In general, I like that Rust has a relatively small standard library because once a feature is in there it stays there forever. 4
2025-05-17 08:00:00
Some people learn new programming languages best by looking at examples for how to do the same thing they know in one language is done in the other. Below is a syntax comparison table which can serve as a quick reference for common C++ constructs and their equivalents in Rust. It is not a comprehensive guide, but I hope it helps out a C++ developer looking for a quick reference to Rust syntax.
Feature | Rust | C++ |
---|---|---|
Immutable Variable Declaration |
let x: i32 = 5; (immutable by default) |
const int x = 5; |
Mutable Variables | let mut x = 5; |
int x = 5; (mutable by default) |
Type Inference | let x = 5; |
auto x = 5; |
Constant Declaration | const MAX: i32 = 100; |
constexpr int MAX = 100; orconsteval int MAX_FN() { return 100; }
|
Function Declaration |
|
|
Implicit Return |
|
|
Immutable Reference | &T |
const T& |
Mutable Reference | &mut T |
T& |
Raw Pointer |
*const T *mut T
|
const T* T*
|
Struct Declaration |
|
|
Struct Initialization | Person { id: uid, health: 100 } |
Person{uid, 100} or Person{.id = uid, .health = 100} (multiple initialization styles available in C++) |
Struct Field Access | person.id |
person.id |
Class/Method Implementation |
|
|
Method with Self |
|
|
Static Method | fn static_method() { /* ... */ } |
static void static_method() { /* ... */ } Note: 'static' in C++ has multiple meanings including file-scope lifetime, class-level function, and non-exported symbols |
Interface/Trait |
|
|
Implementing Interface |
|
|
Generic Function |
|
|
Associated Types |
|
|
Enums (Tagged Union) |
|
|
Pattern Matching |
|
|
Optional Types |
Option<T> (Some(T) or None) |
std::optional<T> |
Error Handling |
Result<T, E> (Ok(T) or Err(E)) |
std::expected<T, E> (C++23) |
Error Propagation | let file = File::open("file.txt")?; |
No direct equivalent; uses exceptions or return codes |
Automatic Trait Implementation | #[derive(Debug, Clone, PartialEq)] |
No direct equivalent (may change with C++26 reflection) |
Memory Management |
|
|
Destructors |
|
|
Serialization | #[derive(Serialize, Deserialize)] |
Requires manual implementation or code generation (may change with C++26 reflection) |
Print to Console | println!("Hello, {name}"); |
std::cout |
Debug Output | println!("{:?}", object); |
No direct equivalent; requires custom implementation |
Pretty Debug Output | println!("{:#?}", object); |
No direct equivalent |
Type | Rust | C++ |
---|---|---|
Boolean | bool |
bool |
Character |
char (Unicode scalar value, 4 bytes) |
char32_t (or wchar_t on some platforms) |
Byte (Raw Data) |
u8 (always unsigned) |
uint8_t or unsigned char (for guaranteed unsigned) |
Unsigned Integers |
|
|
Signed Integers |
|
|
Floating Point | f32, f64 |
float, double |
Unit Type |
() (unit) |
void |
Never Type |
! (never) |
[[noreturn]] void |
Immutable Reference | &T |
const T& |
Mutable Reference | &mut T |
T& |
Raw Pointers |
*const T , *mut T
|
const T* , T*
|
Arrays | [T; N] |
std::array<T, N> or T[N]
|
Slices | &[T] |
std::span<T> (C++20) |
Dynamic Array | Vec<T> |
std::vector<T> |
String |
String (UTF-8 encoded) |
std::string |
String Slice |
&str (UTF-8 encoded) |
std::string_view (C++17) |
C Compatible String |
CString , &CStr
|
std::string , const char*
|
Nullable/Optional | Option<T> |
std::optional<T> (C++17) |
Error Handling | Result<T, E> |
std::expected<T, E> (C++23) |
Smart Pointer (Unique) | Box<T> |
std::unique_ptr<T> |
Smart Pointer (Shared) |
Rc<T> (single-threaded)Arc<T> (thread-safe) |
std::shared_ptr<T> |
Weak Pointer |
Weak<T> (from Rc/Arc) |
std::weak_ptr<T> |
Hash Map | HashMap<K, V> |
std::unordered_map<K, V> |
Ordered Map | BTreeMap<K, V> |
std::map<K, V> |
Hash Set | HashSet<T> |
std::unordered_set<T> |
Ordered Set | BTreeSet<T> |
std::set<T> |
Double-Ended Queue | VecDeque<T> |
std::deque<T> |
Linked List | LinkedList<T> |
std::list<T> |
Priority Queue | BinaryHeap<T> |
std::priority_queue<T> |
Tuple | (T1, T2, ...) |
std::tuple<T1, T2, ...> |
Tagged Union | enum Variant { A(T1), B(T2), ... } |
std::variant<T1, T2, ...> (C++17) |
Interior Mutability |
Cell<T> , RefCell<T>
|
mutable keyword |
Thread-Safe Mutability |
Mutex<T> , RwLock<T>
|
std::mutex + T
|
Atomic Types |
AtomicBool , AtomicUsize , etc. |
std::atomic<bool> , std::atomic<size_t> , etc. |
2025-05-15 08:00:00
Up until a few years ago, Python tooling was a nightmare: basic tasks like installing packages or managing Python versions was a pain. The tools were brittle and did not work well together, mired in a swamp of underspecified implementation defined behaviour.
Then, apparently suddenly, but in reality backed by years of ongoing work on formal interoperability specifications, we saw a renaissance of new ideas in the Python ecosystem. It started with Poetry and pipx and continued with tooling written in Rust like rye, which later got incorporated into Astral.
Astral in particular contributed a very important piece to the puzzle: uv
– an extremely fast Python package and project manager that supersedes all previous attempts;
For example, it is 10x-100x faster than pip.
2025-05-05 08:00:00
I see people make the same mistakes over and over again when learning Rust. Here are my thoughts (ordered by importance) on how you can ease the learning process. My goal is to help you save time and frustration.
Stop resisting. That’s the most important lesson.
Accept that learning Rust requires adopting a completely different mental model than what you’re used to. There are a ton of new concepts to learn like lifetimes, ownership, and the trait system. And depending on your background, you’ll need to add generics, pattern matching, or macros to the list.
Your learning pace doesn’t have much to do with whether you’re smart or not or if you have a lot of programming experience. Instead, what matters more is your attitude toward the language.
I have seen junior devs excel at Rust with no prior training and senior engineers struggle for weeks/months or even give up entirely. Leave your hubris at home.
Treat the borrow checker as a co-author, not an adversary. This reframes the relationship. Let the compiler do the teaching: for example, this works great with lifetimes, because the compiler will tell you when a lifetime is ambiguous. Then just add it but take the time to reason about why the compiler couldn’t figure it out itself.
If you try to compile this, the compiler will ask you to add a lifetime parameter. It provides this helpful suggestion:
1 |
So you don’t have to guess what the compiler wants and can follow its instructions. But also sit down and wonder why the compiler couldn’t figure it out itself.
Most of the time when fighting the compiler it is actually exposing a design flaw. Similarly, if your code gets overly verbose or looks ugly, there’s probably a better way. Declare defeat and learn to do it the Rust way.
If you come from a dynamic language like Python, you’ll find that Rust is more verbose in general. Most of it just comes from type annotations, though. Some people might dismiss Rust as being “unelegant” or “ugly”, but the verbosity actually serves a good purpose and is immensely helpful for building large-scale applications:
Turn on all clippy lints on day one – even the pedantic ones. Run the linter and follow the suggestions religiously. Don’t skip that step once your program compiles.
Resistance is futile. The longer you refuse to learn, the longer you will suffer; but the moment you let your guard down is the moment you’ll start to learn. Forget what you think you knew about programming and really start to listen to what the compiler, the standard library, and clippy are trying to tell you.
I certainly tried to run before I could walk. That alone cost me a lot of precious time.
Don’t make it too hard on yourself in the beginning. Here are some tips:
String
and clone()
and unwrap
generously; you can always refactor later – and refactoring is the best part about Rust!
I wrote an article on saving yourself time during that phase here..and_then
etc. combinatorsDon’t introduce too many new concepts at the same time!
Instead, while you learn about a new concept, have an editor open and write out a few examples.
What helped was to just write some code in the Rust playground and try to get it to compile. Write super small snippets (e.g., one main.rs
for one concept) instead of using one big “tutorial” repo.
Get into the habit of throwing most of your code away.
I still do that and test out ideas in the playground or when I brainstorm with clients.
For instance, here’s one of my favorite code snippets to explain the concept of ownership:
Can you fix it?
Can you explain it?
Ask yourself what would change if v
was an i32
.
If Rust code looks scary to you, break it down. Write your own, simpler version, then slowly increase the complexity. Rust is easier to write than to read. By writing lots of Rust, you will also learn how to read it better as well.
How you do anything is how you do everything. – An ancient Rust proverb
You can be sloppy in other languages, but not in Rust. That means you have to be accurate while you code or the code just won’t compile. The expectation is that this approach will save you debugging time in the future.
I found that the people who learn Rust the fastest all have great attention to detail.
If you try to just get things done and move on, you will have a harder time than if you aim to do things right on your first try.
You will have a much better time if you re-read your code to fix stupid typos before pressing “compile.”
Also build a habit of automatically adding &
and mut
where necessary as you go.
A good example of someone who thinks about these details while coding is Tsoding. For example, watch this stream where he builds a search engine in Rust from scratch to see what I mean. I think you can learn this skill as long as you’re putting in your best effort and give it some time.
With today’s tooling it is very easy to offload the bulk of the work to the computer. Initially, it will feel like you’re making quick progress, but in reality, you just strengthen bad habits in your workflow. If you can’t explain what you wrote to someone else or if you don’t know about the tradeoffs/assumptions a part of your code makes, you took it too far.
Often, this approach stems from a fear that you’re not making progress fast enough. But you don’t have to prove to someone else that you’re clever enough to pick up Rust very quickly.
To properly learn Rust you actually have to write a lot of code by hand. Don’t be a lurker on Reddit, reading through other people’s success stories. Have some skin in the game! Put in the hours because there is no silver bullet. Once it works, consider open sourcing your code even if you know it’s not perfect.
LLMs are like driving a car on auto-pilot. It’s comfortable at first, but you won’t feel in control and slowly, that uneasy feeling will creep in. Turn off the autopilot while learning.
A quick way to set you up for success is to learn by writing code in the Rust Playground first. Don’t use LLMs or code completion. Just type it out! If you can’t, that means you haven’t fully internalized a concept yet. That’s fine! Go to the standard library and read the docs. Take however long it takes and then come back and try again.
Slow is steady and steady is fast.
Muscle memory in programming is highly underrated. People will tell you that this is what code completion is for, but I believe it’s a requirement to reach a state of flow: if you constantly blunder over syntax errors or, worse, just wait for the next auto-completion to make progress, that is a terrible developer experience.
When writing manually, you will make more mistakes. Embrace them! These mistakes will help you learn to understand the compiler output. You will get a “feeling” for how the output looks in different error scenarios. Don’t gloss over these errors. Over time you will develop an intuition about what feels “rustic.”
Another thing I like to do is to run “prediction exercises” where I guess if code will compile before running it. This builds intuition. Try to make every program free of syntax errors before you run it. Don’t be sloppy. Of course, you won’t always succeed, but you will get much better at it over time.
Read lots of other people’s code. I recommend ripgrep
, for example,
which is some of the best Rust code out there.
Don’t be afraid to get your hands dirty. Which areas of Rust do you avoid? What do you run away from? Focus on that. Tackle your blind spots. Track your common “escape hatches” (unsafe, clone, etc.) to identify your current weaknesses. For example, if you are scared of proc macros, write a bunch of them.
After you’re done with an exercise, break it! See what the compiler says. See if you can explain what happens.
A poor personal version is better than a perfect external crate (at least while learning).
Write some small library code yourself as an exercise.
Notable exceptions are probably serde
and anyhow
, which can save you time dealing with JSON inputs and setting up error handling that you can spend on other tasks
as long as you know how they work.
Concepts like lifetimes are hard to grasp. Sometimes it helps to draw how data moves through your system. Develop a habit to explain concepts to yourself and others through drawing. I’m not sure, but I think this works best for “visual”/creative people (in comparison to highly analytical people).
I personally use excalidraw for drawing. It has a “comicy” feel, which takes the edge off a bit. The implication is that it doesn’t feel highly accurate, but rather serves as a rough sketch. Many good engineers (as well as great Mathematicians and Physicists) are able to visualize concepts with sketches.
In Rust, sketches can help to visualize lifetimes and ownership of data or for architecture diagrams.
Earlier I said you should forget everything you know about programming. How can I claim now that you should build on top of what you already know?
What I meant is that Rust is the most different in familiar areas like control flow handling and value passing. E.g., mutability is very explicit in Rust and calling a function typically “moves” its arguments. That’s where you have to accept that Rust is just different and learn from first principles.
However, it is okay to map Rust concepts to other languages you already know. For instance, “a trait is a bit like an interface” is wrong, but it is a good starting point to understand the concept.
Here are a few more examples:
And if you have a functional background, it might be:
Option
is like the Maybe
monad”The idea is that mapping concepts helps fill in the gaps more quickly.
Map what you already know from another language (e.g., Python, TypeScript) to Rust concepts. As long as you know that there are subtle differences, I think it’s helpful.
I don’t see people mention this a lot, but I believe that Rosetta Code is a great resource for that. You basically browse their list of tasks, pick one you like and start comparing the Rust solution with the language you’re strongest in.
Also, port code from a language you know to Rust. This way, you don’t have to learn a new domain at the same time as you learn Rust. You can build on your existing knowledge and experience.
Finally, find other people who come from the same background as you. Read their blogs where they talk about their experiences learning Rust. Write down your experiences as well.
I find that people who tend to guess their way through challenges often have the hardest time learning Rust.
In Rust, the details are everything. Don’t gloss over details, because they always reveal some wisdom about the task at hand. Even if you don’t care about the details, they will come back to bite you later.
For instance, why do you have to call to_string()
on a thing that’s already a string?
my_func
Those stumbling blocks are learning opportunities. It might look like a waste of time to ask these questions and means that it will take longer to finish a task, but it will pay off in the long run.
Reeeeeally read the error messages the compiler prints.
Everyone thinks they do this, but time and again I see people look confused while the solution is right there in their terminal.
There are hints
as well; don’t ignore those.
This alone will save you sooo much time.
Thank me later.
You might say that is true for every language, and you’d be right. But in Rust, the error messages are actually worth your time. Some of them are like small meditations: opportunities to think about the problem at a deeper level.
If you get any borrow-checker errors, refuse the urge to guess what’s going on. Instead of guessing, walk through the data flow by hand (who owns what and when). Try to think it through for yourself and only try to compile again once you understand the problem.
The key to good Rust code is through its type system.
It’s all in the type system. Everything you need is hidden in plain sight. But often, people skip too much of the documentation and just look at the examples.
What few people do is read the actual function documentation. You can even click through the standard library all the way to the source code to read the thing they are using. There is no magic (and that’s what’s so magical about it).
You can do that in Rust much better than in most other languages. That’s because Python for example is written in C, which requires you to cross that language boundary to learn what’s going on. Similarly, the C++ standard library isn’t a single, standardized implementation, but rather has several different implementations maintained by different organizations. That makes it super hard to know what exactly is going on. In Rust, the source code is available right inside the documentation. Make good use of that!
Function signatures tell a lot! The sooner you will embrace this additional information, the quicker you will be off to the races with Rust. If you have the time, read interesting parts of the standard library docs. Even after years, I always learn something when I do.
Try to model your own projects with types first. This is when you start to have way more fun with the language. It feels like you have a conversation with the compiler about the problem you’re trying to solve.
For example, once you learn how concepts like expressions, iterators and traits fit together, you can write more concise, readable code.
Once you learn how to encode invariants in types, you can write more correct code that you don’t have to run to test. Instead, you can’t compile incorrect code in the first place.
Learn Rust through “type-driven development” and let the compiler errors guide your design.
Before you start, shop around for resources that fit your personal learning style. To be honest, there is not that much good stuff out there yet. On the plus side, it doesn’t take too long to go through the list of resources before settling on one specific platform/book/course. The right resource depends on what learner you are. In the long run, finding the right resource saves you time because you will learn quicker.
I personally don’t like doing toy exercises that others have built out for me. That’s why I don’t like Rustlings too much; the exercises are not “fun” and too theoretical. I want more practical exercises. I found that Project Euler or Advent of Code work way better for me. The question comes up quite often, so I wrote a blog post about my favorite Rust learning resources.
I like to watch YouTube, but exclusively for recreational purposes. In my opinion, watching ThePrimeagen is for entertainment only. He’s an amazing programmer, but trying to learn how to program by watching someone else do it is like trying to learn how to become a great athlete by watching the Olympics. Similarly, I think we all can agree that Jon Gjengset is an exceptional programmer and teacher, but watching him might be overwhelming if you’re just starting out. (Love the content though!)
Same goes for conference talks or podcasts: they are great for context, and for soft-skills, but not for learning Rust.
Instead, invest in a good book if you can. Books are not yet outdated and you can read them offline, add personal notes, type out the code yourself and get a “spatial overview” of the depth of the content by flipping through the pages.
Similarly, if you’re serious about using Rust professionally, buy a course or get your boss to invest in a trainer. Of course, I’m super biased here as I run a Rust consultancy, but I truly believe that it will save you and your company countless hours and will set you up for long-term success. Think about it: you will work with this codebase for years to come. Better make that experience a pleasant one. A good trainer, just like a good teacher, will not go through the Rust book with you, but watch you program Rust in the wild and give you personalized feedback about your weak spots.
“Shadow” more experienced team members or friends.
Don’t be afraid to ask for a code review on Mastodon or the Rust forum and return the favor and do code reviews there yourself. Take on opportunities for pair programming.
This is such a great way to see if you truly understood a concept. Don’t be afraid to say “I don’t know.” Then go and explore the answer together by going straight to the docs. It’s way more rewarding and honest.
Help out with OSS code that is abandoned. If you put in a solid effort to fix an unmaintained codebase, you will help others while learning how to work with other people’s Rust code.
Read code out loud and explain it. There’s no shame in that! It helps you “serialize” your thoughts and avoid skipping important details.
Take notes. Write your own little “Rust glossary” that maps Rust terminology to concepts in your business domain. It doesn’t have to be complete and just has to serve your needs.
Write down things you found hard and things you learned. If you find a great learning resource, share it!
If you learn Rust because you want to put it on your CV, stop. Learn something else instead.
I think you have to actually like programming (and not just the idea of it) to enjoy Rust.
If you want to be successful with Rust, you have to be in it for the long run. Set realistic expectations: You won’t be a “Rust grandmaster” in a week but you can achieve a lot in a month of focused effort. There is no silver bullet, but if you avoid the most common ways to shoot yourself in the foot, you pick up the language much faster. Rust is a day 2 language. You won’t “feel” as productive as in your first week of Go or Python, but stick it out and it will pay off. Good luck and have fun!
2025-05-01 08:00:00
We don’t usually think much about Webhooks – at least I don’t. It’s just web requests after all, right? In reality, there is a lot of complexity behind routing webhook requests through the internet.
What if a webhook request gets lost? How do you know it was received in the first place? Can it be a security issue if a webhook gets handled twice? (Spoiler alert: yes)