Latest Posts (16 found)
Corrode 1 weeks ago

Prime Video

Are you one of over 240 million subscribers of Amazon’s Prime Video service? If so, you might be surprised to learn that much of the infrastructure behind Prime Video is built using Rust. They use a single codebase for media players, game consoles, and tablets. In this episode, we sit down with Alexandru Ene, a Principal Engineer at Amazon, to discuss how Rust is used at Prime Video, the challenges they face in building a global streaming service, and the benefits of using Rust for their systems. 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 . Prime Video is a streaming service offered by Amazon that provides a wide range of movies, TV shows, and original content to its subscribers. With over 240 million subscribers worldwide, Prime Video is one of the largest streaming platforms in the world. In addition to its vast content library, Prime Video also offers features such as offline viewing, 4K streaming, and support for multiple devices. On the backend, Prime Video relies on a variety of technologies to deliver its content, including Rust, which is used for building high-performance and reliable systems that can handle the demands of a global audience. Alexandru worked on the transition of Prime Video’s user interface from JavaScript to Rust. He has been with Amazon for over 8 years and previously worked at companies like Ubisoft and EA. He has a background in computer science and is an active open source maintainer. Alexandru lives in London. Ferris Makes Emulators Ep.001 - The Journey Begins - First episode of a famous series where Jake Taylor wrote a Nintendo 64 emulator in Rust from scratch CMake - Very common build system used in C++ applications Conan - C++ Package Manager community project C++ Smart Pointers - Still a footgun Herb Sutter: The Free Lunch Is Over - The seminal 2005 paper that highlights the importance of concurrency, well past C++’s mainstream adoption Rust in Production: cURL - Baseline library used everywhere, written in C, but performant and safe Prime Video Platforms - One app runs on all of these WebAssembly (WASM) - Enabling Rust code with good performance that you can still download and run like JavaScript, avoiding the need for firmware updates on some devices Entity Component System - Used in the UI Rust code for pages in the app Bevy - Game engine written in Rust Leptos - UI framework that makes reactive programming in Rust easier tokio - The de facto standard async runtime for Rust SIMD - A nice feature set some CPUs support WebAssembly Micro Runtime - A tiny WASM runtime well suited for IoT platforms WebAssembly Working Group Amazon Prime Video Rust & WASM for UI: Faster Prime Video on ANY Device - Alexandru Ene, QCon San Francisco 2024 Alexandru Ene on LinkedIn Alexandru’s Blog Alexandru Ene on GitHub

0 views
Corrode 1 months ago

Be Simple

The phone buzzes at 3 AM. You roll out of bed, open your laptop, and see this in the logs: You open the codebase and find this: A few thoughts rush through your head: “What the hell is a PhantomData?” “Why is there a trait object?” “This is going to be a long night.” The error must be buried somewhere in the interaction between that trait, the generic parser, and serde deserialization. You scroll through 200 lines of trait implementations and generic constraints. Each layer adds another level of indirection. The stack trace is 15 levels deep. It’s like peeling an onion… it makes you cry. You run and curse the colleague who wrote this code. Whoops, it was you a few months ago. Quick rewind. The phone buzzes at 3 AM. You roll out of bed, open your laptop, and see this in the logs: You find this code: All right, we seem to be parsing some customer data from a CSV file. You look at line 847 of the input file and see corrupted character encoding. You remove the bad line, deploy a fix, and go back to sleep. Rust programmers tend to be very clever. Too clever for their own good at times. Let me be the first to admit that I’m guilty of this myself. We love to stretch Rust to its limits. After all, this is Rust! An empowering playground of infinite possibility. Shouldn’t we use the language to its full extent? Nothing in Rust forces us to be fancy. You can write straightforward code in Rust just like in any other language. But in code reviews, I often see people trying to outsmart themselves and stumbling over their own shoelaces. They use all the advanced features at their disposal without thinking much about maintainability. Here’s the problem: Writing code is easy. Reading it isn’t. These advanced features are like salt: a little bit can enhance the flavor, but too much can ruin the dish. And advanced features have a tendency to overcomplicate things and make readability harder. Software engineering is all about managing complexity, and complexity creeps in when we’re not looking. We should focus on keeping complexity down. Of course, some complexity is truly unavoidable. That’s the inherent complexity of the task. What we should avoid, however, is the accidental complexity, which we introduce ourselves. As projects grow, accidental complexity tends to grow with them. That is the cruft we all should challenge. And simplicity also has other benefits: Simplicity is prerequisite for reliability. I don’t always agree with Edsger W. Dijkstra, but in this case, he was spot-on. Without simplicity, reliability is impossible (or at least hard to achieve). That’s because simple systems have fewer moving parts to reason about. Good code is mostly boring, especially for production use. Simple is obvious. Simple is predictable. Predictable is good. But if simplicity is so obviously “better,” why isn’t it the norm? Because achieving simplicity is hard! It doesn’t come naturally. Simplicity is typically not the first attempt but the last revision . 1 Simplicity and elegance are unpopular because they require hard work and discipline to achieve. Well put, Edsger. It takes effort to build simple systems. It takes even more effort to keep them simple. That’s because you constantly have to fight entropy . Going from simple to more complex is much easier than the reverse. Let’s come back to our 3 AM phone call. The first version of the code was built by an engineer who wanted to make the system “flexible and extensible.” The second was written by a developer who just wanted to parse a CSV file. Turns out there never once was a need to parse anything other than CSV files. Of course, the original author likely had good reasons for their design. Maybe prior experience might have suggested that more formats would be needed in the future? Or the team wanted to simplify testing by mocking the data format and ended up with a set of abstractions that turned out to be limiting? And shouldn’t the problem have been discovered during code review? These are all valid points! But code reviews also take effort, and it’s easy to miss the forest for the trees. A less experienced Rust developer might not have the experience to evaluate the situation properly and saying “I don’t get it” also takes courage. And even if the code review had caught the complexity, it might have consumed a lot of energy to convince the original author to simplify the code or to change the design because of deadlines or sunk cost fallacies. It’s easy to judge past decisions with hindsight; that’s not my intention. Quite the opposite, actually: a series of individually perfectly reasonable decisions can lead to an overly complex, unmaintainable system. And taken in isolation, each small bit of complexity looks harmless. But complexity quickly compounds. One lesson here is that the path to complexity is paved with good intentions. Another is that senior engineers should pair with junior ones more often to avoid knowledge silos. More experienced developers tend to use more abstractions because they get excited about the possibilities. And I can’t blame them, really. Writing simple code is oftentimes pretty boring. It’s much more fun to test out that new feature we just learned. But after a while we forget how Rust beginners feel about our code: it’s the curse of knowledge . Remember: abstractions are never zero cost. 2 Not all abstractions are created equal. In fact, many are not abstractions at all — they’re just thin veneers, layers of indirection that add complexity without adding real value. – Fernando Hurtado Cardenas Abstractions cause complexity, and complexity has very real consequences. At some point, complexity will slow you down because it causes cognitive load. And cognitive load matters a lot because our mental capacity is finite. The people who are starting with Rust are often overwhelmed by the complexity of the language. Try to keep that in mind as you get more proficient. If you fail to do that, you might alienate team members who are not as experienced as you, and they might give up on the project or Rust altogether. Furthermore, if you switch companies and leave behind a complex codebase, the team will have a hard time maintaining it and onboarding new team members. The biggest holdup is how quickly people will be able to get up to speed with Rust . Don’t make it even harder on them. From time to time, look at Rust through a beginner’s eyes. For some reason I feel compelled to talk about generics for a moment… Not only do they make the code harder to understand, they can also have a real cost on compile times. Each generic gets monomorphized, i.e. a separate copy of the code is generated for each type that is used with that generic at compile time. My advice is to only make something generic if you need to switch out the implementation right now . Resist premature generalization! (Which is related—but not identical to—premature optimization.) “We might need it in the future” is a dangerous statement. The classic example is “we might need to switch databases in the future” leading to over-abstracted data access layers that nobody asked for. Be careful with that assumption because it’s hard to predict the future. 3 Your beautiful abstraction might become your biggest nemesis. If you can defer the decision for just a little longer, it’s often better to do so. Generics also have an impact on the “feel” of the entire codebase. If you use a lot of generics, you will have to deal with the consequences everywhere. You will have to understand the signatures of functions and structs as well as the error messages that come with them. The hidden compilation cost of generics is hard to measure and optimize for. Use generics sparingly. The thinking should be “this is generic functionality” instead of “I could make this generic.” Let’s say you are working on a public API. A function that will be used a lot will need to take some string-based data from the user. You wonder whether you should take a or a or something else as an input to your functions and why. That’s quite simple and doesn’t allocate. But what if the caller wants to pass a String? We take ownership of the input. But hold on, what if we don’t need ownership and we want to support both? That works. But do you see how the complexity goes up? Behind the scenes, it monomorphizes the function for each type that implements . That means that if we pass a and a , we get two copies of that function. That means longer compile times and larger binaries. Wait, what if we need to return something that references the input and we need the result to live as long as the input? Oh wait, we might need to send this across threads: See how we went from a simple parameter to this monstrosity? Each step seemed “reasonable,” but we’ve created a nightmare that nobody wants to read or debug. The problem is so simple, so how did that complexity creep in? We were trying to be clever! We were trying to improve the function by making it more generic. But is it really “better”? All we wanted was a simple function that takes a string and does something with it. Stay simple. Don’t overthink it! Say we’re writing a link checker and we want to build a bunch of requests to check the links. We could use a function that returns a . Or, we could return an iterator instead: The iterator doesn’t look too bad, but the vec is simpler. What to do? The caller likely needs to collect the results anyway. Since we’re processing a finite set of URLs, the link checker needs all results to report successes/failures, and the results will probably be iterated multiple times. Memory usage isn’t a big concern here since the number of URLs in a document is typically small. All else being equal, the vec is probably the simpler choice. There’s a prejudice that simple code is slow. Quite the contrary! It turns out many effective algorithms are surprisingly simple. In fact, some of the simplest algorithms we’ve discovered are also the most efficient. Take quicksort or path tracing, for example. Both can be written down in a handful of lines and described in a few sentences. The idea is pretty simple and can fit on a napkin: Here’s an ad-hoc version of this algorithm in Rust: It’s not too far off from the description. Yes, my simple implementation only supports right now and for any real-world use case, you should use the built-in sort algorithm , which is 20x faster than the above version in my benchmarks, but my point is that simple code packs a punch. On my machine, this implementation sorts 100k numbers in 1 millisecond. This is an O(n log n) algorithm. It’s as fast as it gets for a comparison-based sort and it’s just a few lines of code. 4 Often, simple code can be optimized by the compiler more easily and runs faster on CPUs. That’s because CPUs are optimized for basic data structures and predictable access patterns. And parallelizing work is also easier when that is the case. All of that works in our favor when our code is simple. Somewhat counterintuitively, especially when you’re doing something complicated, you should be extra careful to keep it simple. Simplicity is a sign of deep insight, of great understanding, of clarity—and clarity has a positive effect on the way a system functions. And since complicated systems are, well, complicated, that extra clarity helps keep things under control. What I appreciate about Rust is how it balances high-level and low-level programming. We should leverage that more often. Most of the time, I write Rust code in a straightforward manner, and when that extra bit of performance becomes critical, Rust always lets me go back and optimize. Most of the code you’ll write for companies will be application code, not library code. That’s because most companies don’t make money writing libraries, but business logic. There’s no need to get fancy here. Application code should be straightforward. Library code can be a slightly different story. It can get complicated if it ends up being an important building block for other code. For example, in hot code paths, avoiding allocations might make sense, at which point you might have to deal with lifetimes . This uncertainty about how code might get used by others can lead to overabstraction. Try to make the common case straightforward. The correct path should be the obvious path users take. Say you’re building a base64 encoder. It’s safe to assume that most people will want to encode a string (probably a unicode string like a ) and that they want to use a “canonical” or “standard” base64 encoding. Don’t expect your users to jump through hoops to do the most common thing . Unless you have a really good reason, your API should have a function like this somewhere: Yes, you could make it generic over or support multiple alphabets: …and you might even offer a builder pattern for maximum flexibility: But what most users care about is how to get an encoded string: You could call the function above or to make it clear that it’s a simplified version of a more generic algorithm. Simplicity is especially important when working with other developers because code is a way to communicate ideas, and you should strive to express your ideas clearly. It’s fine to offer additional functionality, but don’t make the easy thing hard in the process. Jerry Seinfeld had two writing modes: creating mode and editing mode . These modes require different mindsets, and trying to do both simultaneously leads to paralysis. As a consequence, Seinfeld would never edit while creating because it would kill the creative flow. The same principle applies to coding. Don’t try to architect the perfect solution on your first attempt. Write the naïve implementation first, then let your inner editor refine it. Switch off that inner critic. Who knows? You might just come up with a simpler design! It can be tempting to use all of these fine, sharp tools you have at your disposal. But sharp tools they are! To master Rust is to say “no” to these tools more often than you say “yes.” You might see an optimization opportunity and feel the urge to jump at it. But time and again, I see people make that optimization without prior validation. The result is that the code performs the same or becomes slower or is otherwise less ideal than the previous version. Measure twice, cut once. That might sound counterintuitive. After all, shouldn’t constant refactoring make our code better as we go? The problem is that we have limited information at the time of writing our first prototype. If we refactor too early, we might end up in a worse place than where we started. Take the CSV exporter from the beginning as an example: a smart engineer saw an opportunity to refactor the code in order to support multiple input formats. That locked us into a place where we had a generic exporter, which became a huge debugging burden while preventing us from seeing a better abstraction had we deferred the refactoring. Maybe we would have noticed that we’re always dealing with CSV data, but we could decouple data validation from data exportation. If we had seen that, it would have led to better error messages like: This opportunity was lost because we jumped the gun and refactored too early. I propose solving the problem at hand first and refactoring afterward. That’s because refactoring a simple program is way easier than doing the same for a complex one. Everyone can do the former, while I can count on one hand those who can do the latter. Preserve the opportunity to refactor your code. Refactoring might look like the smart thing to do at the time, but if you allow the simple code to just stick around for a little longer, the right opportunity for the refactor might present itself. A good time to reflect is when your code starts to feel repetitive. That’s a sign that there’s a hidden pattern in your data. The right abstraction is trying to talk to you! It’s fine to make multiple attempts at an abstraction. See what feels right. If none of it does, just go back to the simple version and document your findings. Rust is super fast, so you can literally commit all the performance crimes you like. Clone liberally, iterate over the same data structure multiple times, use a vector where a hashmap would be better. It simply doesn’t matter. Hardware is fast and cheap, so put it to work. Despite this reality, there’s a noticeable tendency among Rust developers to add significant complexity or extra code to avoid any and all allocations in code that isn’t even performance-critical. I think that’s because Rust is very anal about memory management, which makes developers hyper-aware of these costs. This awareness can get in the way of simplicity. Sometimes using or is not worth optimizing away if it keeps the code simple. If you’d like to learn more, I gave a talk on that topic titled The Four Horsemen of Bad Rust Code . All of the above doesn’t mean you should not learn about the good abstractions. It’s fun to learn and to be knowledgeable. But you can focus on learning new concepts without hurting yourself. Understanding macros, lifetimes, interior mutability, etc. is very helpful, but in everyday “normal” Rust code you almost never make use of these concepts, so don’t worry about them too much and keep moving. Use all the features you need and none you don’t. Crucially, writing simple software does not mean implementing “good-enough-for-now” software. That approach surely will lead to technical debt and maintenance nightmares further down the line as workarounds get stacked on top of each other. No, simple software can still be well-designed software, but it keeps the current requirements in mind. The goal is not to take shortcuts. Quite the contrary: the goal is to avoid unnecessary complexity, which often is the root cause for having to take shortcuts later on when bad systems design prevents a cleaner solution. In code reviews , I pay extra attention to ensuring we don’t cut corners to avoid doing the necessary design work. One litmus test I like to use is “Does it feel good to add new functionality?” Good abstractions tend to “click” together. It just feels like there’s no overlap between the abstractions and no grunt work or extra conversions needed. The next step always feels obvious. Testing works without much mocking, your documentation for your structs almost writes itself. There’s no “this struct does X and Y” in your documentation. It’s either X or Y. Explaining the design to a fellow developer is straightforward. This is when you know you have a winner. Getting there is not easy. It can take many iterations. The result of that process is what you’ll often see in popular libraries. The right abstractions guide you to do the right thing: to find the obvious place to add new functionality, the right place to look for a bug, the right spot to make that database query. All of that is easier if the code is simple. That’s why experienced developers always have simplicity in mind when they build out abstractions. It’s possible that the original sin of a smart engineer is to optimize a thing that should not exist in the first place. Cross that bridge when you get there. Be clear, not clever. Write code for humans, not computers. Simplicity is clarity. Simplicity is to succinctly express the essence of a thing. Simplicity is about removing the unnecessary, the irrelevant, the noise. Simple is good. Be simple. I think there’s a similarity to writing here, where elegance (which is correlated with simplicity, in my opinion) requires an iterative process of constant improvement. The editing process is what makes most writing great. In 1657, Blaise Pascal famously wrote: “I have only made this letter longer because I have not had the time to make it shorter.” I think about that a lot when I write. ↩ For reference, see “The Power of 10 Rules” by Gerard J. Holzmann of the NASA/JPL Laboratory for Reliable Software. ↩ I should know because I passed on a few very risky but lucrative investment opportunities because I lacked the ability to accurately predict the future. ↩ Of course, this is not the most efficient implementation of quicksort. It allocates a lot of intermediate vectors and has O(n^2) worst-case performance. There are optimizations for partially sorted data, better pivot selection strategies, and in-place partitioning. But they are just that: optimizations. The core idea remains the same. ↩

0 views
Corrode 2 months ago

Season 4 - Finale

It’s time for another recap including our highlights of Season 4. We’ve been at this for a while now (four seasons, and 32 episodes to be exact). We had guests from a wide range of industries: from Microsoft to Astral, and from password managers to satellite systems. This time, it’s all about using Rust for foundational software, which is software that is critical to a team or even an entire organization. Rust is a great fit for this type of software!

0 views
Corrode 3 months ago

KSAT

As a kid, I was always fascinated by space tech. That fascination has only grown as I’ve learned more about the engineering challenges involved in space exploration. In this episode, we talk to Vegard Sandengen, a Rust engineer at KSAT, a company that provides ground station services for satellites. They use Rust to manage the data flow from hundreds of satellites, ensuring that data is received, processed, and stored efficiently. This data is then made available to customers around the world, enabling them to make informed decisions based on real-time satellite data. We dive deep into the technical challenges of building reliable, high-performance systems that operate 24/7 to capture and process satellite data. Vegard shares insights into why Rust was chosen for these mission-critical systems, how they handle the massive scale of data processing, and the unique reliability requirements when dealing with space-based infrastructure. From ground station automation to data pipeline optimization, this conversation explores how modern systems programming languages are enabling the next generation of space technology infrastructure. 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 . KSAT, or Kongsberg Satellite Services, is a global leader in providing ground station services for satellites. The company slogan is “We Connect Space And Earth,” and their mission-critical services are used by customers around the world to access satellite data for a wide range of applications, including weather monitoring, environmental research, and disaster response. Vegard Sandengen is a Rust engineer at KSAT, where he works on the company’s data management systems. He has a Master’s degree in computer science and has been working in the space industry for several years. At KSAT, Vegard focuses on building high-performance data processing pipelines that handle satellite telemetry and payload data from ground stations around the world. His work involves optimizing real-time data flows and ensuring system reliability for mission-critical space operations.

0 views
Corrode 3 months ago

1Password

Handling secrets is extremely hard. You have to keep them safe (obviously), while at the same time you need to integrate with a ton of different systems and always provide a great user-experience, because otherwise people will just find a way around your system. When talking to peers, a lot of people mention 1Password as a company that nailed this balance. In today’s episode, I talk to Andrew about how 1Password uses Rust to build critical systems that must never fail, how Rust helps them handle secrets for millions of users, and the lessons they learned when adopting Rust in their stack. 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 . 1Password is a password manager that helps users securely store and manage their passwords, credit card information, and other sensitive data. It provides a user-friendly interface and strong security features to protect users’ secrets across multiple devices. Andrew is a Senior Rust Developer at 1Password in the Product Foundations org, on the Frameworks team and specifically on the Core Platform squad handling the asynchronous frameworks other developers use to build features (i.e. requests into the Rust core from the Native clients, data sync, etc.). He specifically specialized in that synchronization process, getting data federated from cloud to clients to native apps and back.

0 views
Corrode 4 months ago

Tembo

Recently I was in need of a simple job queue for a Rust project. I already had Postgres in place and wondered if I could reuse it for this purpose. I found PGMQ by Tembo . PGMQ a simple job queue written in Rust that uses Postgres as a backend. It fit the bill perfectly. In today’s episode, I talk to Adam Hendel , the founding engineer of Tembo, about one of their projects, PGMQ, and how it came to be. We discuss the design decisions behind job queues, interfacing from Rust to Postgres, and the engineering decisions that went into building the extension. It was delightful to hear that you could build all of this yourself, but that you would probably just waste your time doing so and would come up with the same design decisions as Adam and the team. 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 . Tembo builds developer tools that help teams build and ship software faster. Their first product, PGMQ , was created to solve the problem of job queues in a simple and efficient way, leveraging the power of Postgres. They since made a pivot to focus on AI-driven code assistance, but PGMQ can be used independently and is available as an open-source project. Adam Hendel is the founding engineer at Tembo, where he has been instrumental in developing PGMQ and other tools like pg_vectorize . He has since moved on to work on his own startup, but remains involved with the PGMQ project.

0 views
Corrode 4 months ago

Rust For Foundational Software

Ten years of stable Rust; writing this feels surreal. It’s only been yesterday that we all celebrated the 1.0 release of this incredible language. I was at Rust Week where Niko Matsakis gave his talk “Our Vision for Rust” in which he made a profound and insightful statement: Rust is a language for building foundational software . That deeply struck me. I highly recommend you read his blog post titled “Rust in 2025: Targeting foundational software” , which is a great summary on the topic. I wanted to expand on the idea and share what this means to corrode (and perhaps to a wider extent to Rust in the industry). First off, do we really need another term? After all, many people still think of Rust as a systems programming language first and foremost, so why can’t we just stick to “systems programming”? I believe the framing is all wrong. From the outside, “systems programming” might establish that it is about “building systems,” but the term is loaded with historical baggage that feels limiting and prohibitive. It creates an artificial distinction between systems programming and “other types of programming.” The mindset “We are not a systems programming company so we don’t need Rust” is common, but limiting. If I may be candid for a moment, I believe well-known systems-programming domains have a tendency to be toxic. Even the best developers in the world have had that experience. The first contribution that I had to the Linux kernel was some fix for the ext3 file system. It was a very emotional moment for me. I sent a patch to the Linux Kernel and then I saw an email response from Al Viro - one of those developers I’d only heard about and dreamed of meeting someday. He responded, ‘I’ve never seen code this bad in my life. You managed to introduce three new bugs in two new lines of code. People like you should never be allowed to get close to a keyboard again.’ That was my introduction to Linux. – Glauber Costa, co-founder of Turso, on the Top Shelf Podcast Glauber went on to work at Red Hat, Parallels, ScyllaDB, and Datadog on schedulers, databases, and performance optimizations, but just imagine how many capable developers got discouraged by similar early feedback or never even tried to contribute to the Linux kernel in the first place. I find that ironic because people once dismissed Linux itself as just a toy project . The Linux kernel wasn’t built in a day. People need time to learn. The whole idea of Rust is to enable everyone to build reliable and efficient software. To me, it’s about breaking down the barriers to entry and making larger parts of the software stack accessible to more people. You can sit with us. We are committed to providing a friendly, safe and welcoming environment for all – The Rust Code of Conduct That is also where the idea for corrode comes from: To cut through red tape in the industry. The goal is to gradually chip away at the crust of our legacy software with all its flaws and limitations and establish a better foundation for the future of infrastructure. To try and defy the ‘common wisdom’ about what the tradeoffs have to be . The term corrode is Latin for “to gnaw to bits, wear away.” 1 “I think ‘infrastructure’ is a more useful way of thinking about Rust’s niche than arguing over the exact boundary that defines ‘systems programming’.” “This is the essence of the systems Rust is best for writing: not flashy, not attention-grabbing, often entirely unnoticed. Just the robust and reliable necessities that enable us to get our work done, to attend to other things, confident that the system will keep humming along unattended.” – Graydon Hoare, 10 Years of Stable Rust: An Infrastructure Story In conversations with potential customers, one key aspect that comes up with Rust a lot is this perception that Rust is merely a systems programming language. They see the benefit of reliable software, but they don’t see how Rust could help them. I used to reply along the lines of Rust’s mantra: “empowering everyone to build reliable and efficient software” – and while I love this mission, it didn’t always “click” with people. In practice, my clients use Rust for a much broader range of software , not just kernel drivers and embedded code! They use Rust for writing software that underpins other software . Then I used to tell my own story: I did some C++ in the past, but I wouldn’t call myself a hardcore systems person. And yet, I help a lot of clients with really interesting and complex pieces of software. I ship code that is used by many people and companies like Google, Microsoft, AWS, and NVIDIA. Rust is a great enabler, a superpower, a fireflower . I found that my clients often don’t use Rust as a C++ replacement. Many of them don’t even have any C++ in production to begin with. They also don’t need to work on the hardware-software interface or spend their time in low-level code. What all clients have in common, however, is that the services they build with Rust are foundational to their core business . Rust is used for building platforms: systems which enable building other systems on top. These services need to be robust and reliable and serve as platforms for other code that might or might not be written in Rust. This is, in my opinion, the core value proposition of Rust: to build things that form the bedrock of critical infrastructure and must operate reliably for years. Rust is a day-2 language, i.e. it only starts to shine after taking a leap of faith and using it for an extensive period of time. In Rust, all of the problems that you have during the lifecycle of your application surface early in development. Once a service hits production, maintaining it is a breeze. There is very little on-call work. The focus should be on what Rust enables: a way to express very complicated ideas on a type-system level, which will help build complex abstractions through simple core mechanics: ownership, borrowing, lifetimes, and its trait system. This mindset takes away the focus from Rust as a C++ replacement and also explains why so many teams which use languages like Python, TypeScript, and Kotlin are attracted to Rust. What is less often talked about is that Rust is a language that enables people to move across domain boundaries: from embedded to cloud, from data science to developer tooling. Few other languages are so versatile and none offer the same level of correctness guarantees. If you know Rust, you can program simple things in all of these domains. But don’t we just replace “Systems Programming” with “Foundational Software”? Does using the term “Foundational Software” simply create a new limiting category? Crucially, foundational software is different from low-level software and systems software. For my clients, it’s all foundational. For example, building a data plane is foundational. Writing a media-processing pipeline is foundational. Rust serves as a catalyst: companies start using it for critical software but then, as they get more comfortable with the language, expand into using it in other areas of their business: I’ve seen it play out as we built Aurora DSQL - we chose Rust for the new dataplane components, and started off developing other components with other tools. The control plane in Kotlin, operations tools in TypeScript, etc. Standard “right tool for the job” stuff. But, as the team has become more and more familiar and comfortable with Rust, it’s become the way everything is built. A lot of this is because we’ve seen the benefits of Rust, but at least some is because the team just enjoys writing Rust. – Marc Brooker, engineer at Amazon Web Services in Seattle on lobste.rs That fully aligns with my experience: I find that teams become ambitious after a while. They reach for loftier goals because they can . The fact they don’t have to deal with security issues anymore enables better affordances. From my conversations with other Rustaceans, we all made the same observation: suddenly we can build more ambitious projects that we never dared tackle before. It feels to me as if this direction is more promising: starting with the foundational tech and growing into application-level/business-level code if needed/helpful. That’s better than the other way around, which often feels unnecessarily clunky. Once the foundations are in Rust, other systems can be built on top of it. Just because we focus on foundational software doesn’t mean we can’t do other things. But the focus is to make sure that Rust stays true to its roots. So, what is foundational software? It’s software that organizations deem critical for their success. It might be: and many, many more. All of these things power organizations and must not fail or at least do so gracefully . My clients and the companies I interviewed on our podcast all have one thing in common: They work on Rust projects that are not on the sideline, but front and center, and they shape the future of their infrastructure. Rust is useful in situations where the “worse is better” philosophy falls apart ; it’s a language for building the “right thing”: With the right thing, designers are equally concerned with simplicity, correctness, consistency, and completeness. I think many companies will choose Rust to build their future platforms on. As such, it competes with C++ as much as it does with Kotlin or Python. I believe that we should shift the focus away from memory safety (which many other languages also offer) and instead focus on the explicitness, expressiveness, and ecosystem of Rust that is highly competitive with these languages. It is a language for teams which want to build things right and are at odds with the “move fast and break things” philosophy of the past. Rust is future-looking. Backwards-compatibility is enforced by the compiler and many people work on the robustness aspect of the language. Dropbox was one of the first production users of Rust. They built their storage layer on top of it. At no point did they think about using Rust as a C++ replacement. Instead, they saw the potential of Rust as a language for building scalable and reliable systems. Many more companies followed suit: Amazon, Google, Microsoft, Meta, Discord, Cloudflare, and many more. These organizations build platforms and Rust, a tool for professional programmers, developed by world experts over more than a decade of hard work, is best equipped to fit the bill. Is Rust used for real? “At this point, we now know the answer: yes, Rust is used a lot. It’s used for real, critical projects to do actual work by some of the largest companies in our industry. We did good.” “[Rust is] not a great hobby language but it is a fantastic professional language, precisely because of the ease of refactors and speed of development that comes with the type system and borrow checker.” – Graydon Hoare, 10 Years of Stable Rust: An Infrastructure Story To build a truly industrial-strength ecosystem, we need to remember the professional software lifecycle, which is often measured in decades. Stability plays a big role in that. The fact that Rust has stable editions and a language specification is crucial for industry adoption. But Rust is more than just a compiler and its standard library: the tooling and wider ecosystem are equally important. To build foundational software, you need guarantees that vulnerabilities get fixed and that the ecosystem evolves and adapts to the customer’s needs. The ecosystem is still mostly driven by volunteers who maintain key libraries and tools in their free time. There is more to be said about supply-chain security and sustainability in a future post. Building foundational systems is rooted in the profound belief that the efforts will pay off in the long run because organizations and society will benefit from them for decades. We are building systems that will be used by people who may not even know they are using them, but who will depend on them every day; that’s critical infrastructure. And Rust allows us to do so with great ergonomics. Rust inherited the pragmatism from C++ and the purism from Haskell. Rust enables us to build sustainable software that stays within its means and is concerned about low resource usage. Systems where precision and correctness matter. Solutions that work across language boundaries and up and down the stack. Rust is a language for decades and my mission is to be a part of this shift. On to the next 10 years! Conveniently, it also has a ‘C’ and an ‘R’ in the name, which bridges both languages. ↩

0 views
Corrode 4 months ago

Rust

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.

0 views
Corrode 4 months ago

Sharp Edges In The Rust Standard Library

The Rust standard library , affectionately called , 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 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. 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 it. For more details on the topic, check out matklad’s article: “Join Your Threads” . In fact, there was a proposal to make be , 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 is the easiest way of spawning threads, but not the best one for casual use. Manually calling is a chore and easy to forget, which makes a potential footgun. For new code, I recommend using 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- 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. 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 . 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 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 . Even the documentation of itself agrees: NOTE: It is almost always better to use or 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, is still a nightly-only feature 2 : Even if you wanted a linked list, it probably would not be : 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 as well, but I leave it at that. 3 does a decent job of abstracting away the underlying file system. One thing I always disliked was that returns a instead of a . I mentioned in my ‘Pitfalls of Safe Rust’ article that joining a relative path with an absolute path results in the absolute path being returned. 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 . That’s always a fun dance: These operations must be repeated everywhere. It makes path manipulation every so slightly annoying. There are a few more issues with paths in Rust: Of course, for everyday use, is perfectly okay, but if path handling is a core part of your application, you might want to consider using an external crate instead. 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 is platform dependent, which causes some headaches . Same for , 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 : The documentation does not specify the clock’s accuracy or how it handles leap seconds, except to note that 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 works well in combination with the rest of the standard library, such as for : …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. There are a few options: is a new library, which aims to improve upon the existing libraries. 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 For a more in-depth discussion on why the Linux kernel uses linked lists, see this article . ↩ Perhaps this is a little surprising if you mostly use vectors, but removing an element from a list is an operation. ↩ “Hold on, what’s wrong with BTreeMap?” you might ask. Okay, as you know, Rust has two map implementations in the standard library: , which guarantees insertion ordering, while is unordered, but more commonly used. For a long time, a “performance trick” was to use if you needed a faster hash map implementation. Since then, the performance of has improved significantly. One reason is that the implementation of has changed to use a “siphash” algorithm and is now based on Google’s SwissTable . The combination of these changes has made much more performant than before, so there is no good reason to use anymore, other than the ordering guarantee. One important distinction to note: iteration order over a is random, while ’s iteration order is always sorted by the key’s implementation (not by insertion order). This makes useful when you need to iterate over keys in a sorted manner. If you’re aggregating data in a but need a sorted list, you’ll need to collect into a vector and sort it manually. In contrast, gives you sorted iteration for free. So while is better for random access operations, is still helpful when sorted iteration is required. I would argue that it’s a bit of a niche use case, however. In “Smolderingly fast b-trees” , Jamie Brandon compares the performance of Rust’s and . Here are the key takeaways: I’d argue that a normal HashMap is almost always the better choice and having two map implementations in the standard library can be confusing. On top of that, if the hash map is your bottleneck, you’re doing pretty well already. If you need anything faster, there are plenty of great external crates like indexmap for insertion-order preservation and dashmap for concurrent access. As I said, nothing earth-shaking, but I think it’s worth mentioning that there are better alternatives to out there in the ecosystem. Yes, you can deprecate functionality, but this is a very timid and laborious process and that still doesn’t mean functionality gets removed. For example, has been deprecated for years and is now not getting removed, but instead will be fixed with a bugfix release and un-deprecated . ↩

0 views
Corrode 5 months ago

C++ to Rust Cheat-Sheet

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.

0 views
Corrode 5 months ago

Astral

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: – an extremely fast Python package and project manager that supersedes all previous attempts; For example, it is 10x-100x faster than pip.

0 views
Corrode 5 months ago

Flattening Rust's Learning Curve

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: 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: Don’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 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 was an . 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 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 , 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 and , 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: 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 on a thing that’s already a string? 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 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!

0 views
Corrode 5 months ago

Svix

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)

0 views
Corrode 6 months ago

Microsoft

Victor Ciura is a veteran C++ developer who worked on Visual C++ and the Clang Power Tools. In this first episode of season 4, we talk to him about large-scale Rust adoption at Microsoft.

0 views
Corrode 6 months ago

Pitfalls of Safe Rust

When people say Rust is a “safe language”, they often mean memory safety. And while memory safety is a great start, it’s far from all it takes to build robust applications. Memory safety is important but not sufficient for overall reliability. In this article, I want to show you a few common gotchas in safe Rust that the compiler doesn’t detect and how to avoid them. Even in safe Rust code, you still need to handle various risks and edge cases. You need to address aspects like input validation and making sure that your business logic is correct. Here are just a few categories of bugs that Rust doesn’t protect you from: Let’s look at ways to avoid some of the more common problems. The tips are roughly ordered by how likely you are to encounter them. Overflow errors can happen pretty easily: If and are large enough, the result will overflow. Rust will panic in debug mode, but in release mode, it will silently wrap around. To avoid this, use checked arithmetic operations: Static checks are not removed since they don’t affect the performance of generated code. So if the compiler is able to detect the problem at compile time, it will do so: The error message will be: For all other cases, use , , , and , which return instead of wrapping around on underflow or overflow. 1 Quick Tip: Enable Overflow Checks In Release Mode Rust carefully balances performance and safety. In scenarios where a performance hit is acceptable, memory safety takes precedence. 1 Integer overflows can lead to unexpected results, but they are not inherently unsafe. On top of that, overflow checks can be expensive, which is why Rust disables them in release mode. 2 However, you can re-enable them in case your application can trade the last 1% of performance for better overflow detection. Put this into your : This will enable overflow checks in release mode. As a consequence, the code will panic if an overflow occurs. See the docs for more details. One example where Rust accepts a performance cost for safety would be checked array indexing, which prevents buffer overflows at runtime. Another is when the Rust maintainers fixed float casting because the previous implementation could cause undefined behavior when casting certain floating point values to integers. ↩ According to some benchmarks, overflow checks cost a few percent of performance on typical integer-heavy workloads. See Dan Luu’s analysis here ↩ While we’re on the topic of integer arithmetic, let’s talk about type conversions. Casting values with is convenient but risky unless you know exactly what you are doing. There are three main ways to convert between numeric types in Rust: ⚠️ Using the keyword: This approach works for both lossless and lossy conversions. In cases where data loss might occur (like converting from to ), it will simply truncate the value . Using : This method only allows lossless conversions . For example, you can convert from to since all 32-bit integers can fit within 64 bits. However, you cannot convert from to using this method since it could potentially lose data. Using : This method is similar to but returns a instead of panicking. This is useful when you want to handle potential data loss gracefully. Quick Tip: Safe Numeric Conversions If in doubt, prefer and over . ( Adapted from StackOverflow answer by delnan and additional context . ) The operator is not safe for narrowing conversions . It will silently truncate the value, leading to unexpected results. What is a narrowing conversion? It’s when you convert a larger type to a smaller type, e.g. to . For example, see how chops off the high bits from our value: So, coming back to our first example above, instead of writing use instead and handle the error gracefully: Bounded types make it easier to express invariants and avoid invalid states. E.g. if you have a numeric type and 0 is never a correct value, use instead. You can also create your own bounded types: ( Rust Playground ) Whenever I see the following, I get goosebumps 😨: That’s a common source of bugs. Unlike C, Rust does check array bounds and prevents a security vulnerability, but it still panics at runtime . Instead, use the method: It returns an which you can now handle gracefully. See this blog post for more info on the topic. This issue is related to the previous one. Say you have a slice and you want to split it at a certain index. You might expect that this returns a tuple of slices where the first slice contains all elements and the second slice is empty. Instead, the above code will panic because the mid index is out of bounds! To handle that more gracefully, use instead: This returns an which allows you to handle the error case. ( Rust Playground ) More info about here . It’s very tempting to use primitive types for everything. Especially Rust beginners fall into this trap. However, do you really accept any string as a valid username? What if it’s empty? What if it contains emojis or special characters? You can create a custom type for your domain instead: ( Rust playground ) The next point is closely related to the previous one. Can you spot the bug in the following code? The problem is that you can have set to but set to . That’s an invalid state! If you try to use the SSL connection, you can’t because there’s no certificate. This issue can be detected at compile-time: Use types to enforce valid states: In comparison to the previous section, the bug was caused by an invalid combination of closely related fields . To prevent that, clearly map out all possible states and transitions between them. A simple way is to define an enum with optional metadata for each state. If you’re curious to learn more, here is a more in-depth blog post on the topic . It’s quite common to add a blanket implementation to your types. But that can lead to unforeseen issues. For example, here’s a case where the port is set to 0 by default, which is not a valid port number. 2 Instead, consider if a default value makes sense for your type. If you blindly derive for your types, you might expose sensitive data. Instead, implement manually for types that contain sensitive information. Instead, you could write: This prints ( Rust playground ) For production code, use a crate like . However, it’s not black and white either: If you implement manually, you might forget to update the implementation when your struct changes. A common pattern is to destructure the struct in the implementation to catch such errors. Instead of this: How about destructuring the struct to catch changes? ( Rust playground ) Thanks to Wesley Moore (wezm) for the hint and to Simon Brüggen (m3t0r) for the example. Don’t blindly derive and – especially for sensitive data. The values you read/write might not be what you expect! When deserializing, the fields might be empty. Empty credentials could potentially pass validation checks if not properly handled On top of that, the serialization behavior could also leak sensitive data. By default, will include the password field in the serialized output, which could expose sensitive credentials in logs, API responses, or debug output. A common fix is to implement your own custom serialization and deserialization methods by using . The advantage is that you have full control over input validation. However, the disadvantage is that you need to implement all the logic yourself. An alternative strategy is to use the attribute. Let’s take the field as an example. Start by using the newtype pattern to wrap the standard types and add custom validation: Now implement for : With this trick, you can no longer deserialize invalid passwords: (Try it on the Rust Playground ) Credits go to EqualMa’s article on dev.to and to Alex Burka (durka) for the hint. This is a more advanced topic, but it’s important to be aware of it. TOCTOU (time-of-check to time-of-use) is a class of software bugs caused by changes that happen between when you check a condition and when you use a resource. ( Rust playground ) The safer approach opens the directory first, ensuring we operate on what we checked: ( Rust playground ) Here’s why it’s safer: while we hold the handle, the directory can’t be replaced with a symlink. This way, the directory we’re working with is the same as the one we checked. Any attempt to replace it won’t affect us because the handle is already open. You’d be forgiven if you overlooked this issue before. In fact, even the Rust core team missed it in the standard library. What you saw is a simplified version of an actual bug in the function. Read more about it in this blog post about CVE-2022-21658 . Timing attacks are a nifty way to extract information from your application. The idea is that the time it takes to compare two values can leak information about them. For example, the time it takes to compare two strings can reveal how many characters are correct. Therefore, for production code, be careful with regular equality checks when handling sensitive data like passwords. Protect Against Denial-of-Service Attacks with Resource Limits. These happen when you accept unbounded input, e.g. a huge request body which might not fit into memory. Instead, set explicit limits for your accepted payloads: If you use to join a relative path with an absolute path, it will silently replace the relative path with the absolute path. This is because will return the second path if it is absolute. I was not the only one who was confused by this behavior. Here’s a thread on the topic , which also includes an answer by Johannes Dahlström : The behavior is useful because a caller […] can choose whether it wants to use a relative or absolute path, and the callee can then simply absolutize it by adding its own prefix and the absolute path is unaffected which is probably what the caller wanted. The callee doesn’t have to separately check whether the path is absolute or not. And yet, I still think it’s a footgun. It’s easy to overlook this behavior when you use user-provided paths. Perhaps should return a instead? In any case, be aware of this behavior. So far, we’ve only covered issues with your own code. For production code, you also need to check your dependencies. Especially unsafe code would be a concern. This can be quite challenging, especially if you have a lot of dependencies. cargo-geiger is a neat tool that checks your dependencies for unsafe code. It can help you identify potential security risks in your project. This will give you a report of how many unsafe functions are in your dependencies. Based on this, you can decide if you want to keep a dependency or not. Here is a set of clippy lints that can help you catch these issues at compile time. See for yourself in the Rust playground . Here’s the gist: Phew, that was a lot of pitfalls! How many of them did you know about? Even if Rust is a great language for writing safe, reliable code, developers still need to be disciplined to avoid bugs. A lot of the common mistakes we saw have to do with Rust being a systems programming language: In computing systems, a lot of operations are performance critical and inherently unsafe. We are dealing with external systems outside of our control, such as the operating system, hardware, or the network. The goal is to build safe abstractions on top of an unsafe world. Rust shares an FFI interface with C, which means that it can do anything C can do. So, while some operations that Rust allows are theoretically possible, they might lead to unexpected results. But not all is lost! If you are aware of these pitfalls, you can avoid them, and with the above clippy lints, you can catch most of them at compile time. That’s why testing, linting, and fuzzing are still important in Rust. For maximum robustness, combine Rust’s safety guarantees with strict checks and strong verification methods. Let an Expert Review Your Rust Code I hope you found this article helpful! If you want to take your Rust code to the next level, consider a code review by an expert. I offer code reviews for Rust projects of all sizes. Get in touch to learn more. There’s also methods for wrapping and saturating arithmetic, which might be useful in some cases. It’s worth it to check out the documentation to learn more. ↩ Port 0 usually means that the OS will assign a random port for you. So, is valid, but it might not be supported on all operating systems or it might not be what you expect. See the docs for more info. ↩

0 views
Corrode 7 months ago

Rust Learning Resources 2025

Want to finally learn Rust? When I ask developers what they look for in a Rust learning resource, I tend to get the same answers: All of the above are valid points, especially for learning Rust – a language known for its notoriously steep learning curve. If you’ve been thinking about learning Rust for a while now and perhaps you’ve already started dabbling with it, now’s the time to fully commit. I’ve put together my favorite Rust learning resources for 2025 to help you jumpstart your Rust journey. I made sure to include a healthy mix of self-paced resources, interactive exercises, and hands-on workshops. Key Points: The classic Rust learning resource. If it was a cocktail, it would be an Old Fashioned . Rustlings works great for beginners and for anyone wanting a quick refresher on specific Rust concepts. You can run Rustlings from your command line, and it guides you through a series of exercises. All it takes is running a few commands in your terminal: Go to the official Rustlings repository to learn more. Key Points: Rustfinity is a bit like Rustlings, but more modern and structured. It gives you a browser interface that guides you through each exercise and runs tests to check your solutions. No need to set up anything locally, which makes it great for workshops or learning on the go. You start with “Hello, World!” and work your way up to more complex exercises. It’s a relatively new resource, but I’ve tried several exercises myself and enjoyed the experience. They also host “Advent of Rust” events with some more challenging problems available here . Learn more at the Rustfinity website . Key Points: This is another relatively new resource by Luca Palmieri, who is the author of the popular “Zero To Production” book. It’s a collection of 100 exercises that help you learn Rust. The course is based on the “learning by doing” principle and designed to be interactive and hands-on. You can work through the material in your browser, or download a PDF or buy a printed copy for offline reading. The course comes with a local CLI tool called that verifies your solutions. You can learn more about the course here . Key Points: If you already know how to code some Rust and want to take it one step further, CodeCrafters is currently the best resource for learning advanced Rust concepts in your own time. I like that they focus on real-world systems programming projects, which is where Rust is really strong. You’ll learn how to build your own shell, HTTP server, Redis, Kafka, Git, SQLite, or DNS server from scratch. Most people work on the projects on evenings and weekends, and it takes a few days or weeks to complete a project, but the sense of accomplishment when you finish is incredible: you’ll complete a project that teaches you both Rust and the inner workings of systems you use daily. Try CodeCrafters For Free CodeCrafters is the platform I genuinely recommend to friends after they’ve learned the basics of Rust. It’s the next best thing after a personal mentor or workshop. You can try CodeCrafters for free here and get 40% off if you upgrade to a paid plan later. Full disclosure: I receive a commission for new subscriptions, but I would recommend CodeCrafters even if I didn’t. On top of that, most companies will reimburse educational resources through their L&D budget, so check with your manager about getting reimbursed. Key Points: I’m biased here, but nothing beats a hands-on workshop with a Rust expert. Learning from experienced Rust developers is probably the quickest way to get up to speed with Rust – especially if you plan to use Rust at work. That’s because trainers can provide you with personalized feedback and guidance, and help you avoid common pitfalls. It’s like having a personal trainer for your Rust learning journey. My workshops are designed to be hands-on and tailored to the needs of the participants. I want people to walk away with a finished project they can extend. All course material is open source and freely available on GitHub: You can go through the material on your own to see if it fits your needs. Once you’re ready, feel free to reach out about tailoring the content for you and your team. Speed Up Your Learning Process Is your company considering a switch to Rust? Rust is known for its steep learning curve, but with the right resources and guidance, you can become proficient in a matter of weeks. I offer hands-on workshops and training for teams and individuals who want to accelerate their learning process. Check out my services page or send me an email to learn more.

0 views