Before we start:

This post is long and detailed on purpose. It's not meant to be read linearly or "from start to finish". If you're just getting started, it's perfectly fine to read only the parts that feel useful right now and ignore the rest until later


I

I

I

Have you ever opened the rust-lang/rust repo and felt overwhelmed by the sheer number of files, tests, and issues? Like you're stuck in "analysis paralysis" - so many options and so much complexity that you freeze and don't know where to start. If you feel ambitious and/or have time and energy to help people around the world and support your favorite programming language? Please, pull up a chair - this story is for you!

To be honest, I personally didn't have that stuck feeling at start, but most people who ask me how to get started do

Finding Something to Work On

First, let's go to the rust-lang/rust repo. (Important note: this applies to other Rust repos like clippy or rustfmt too - they also need contributors) Now stop for a moment and ask yourself: do you know what you want to work on?

If your answer is "no", that's totally fine! A good first step is to start looking for an interesting task in this huge space of issues. There are tons of things to do - some exciting and some more routine. Here are a few beginner-friendly areas you might start with:

  1. Diagnostics: This is the part of the compiler that handles our error messages. You can help make these messages clearer or fix cases where the current message is confusing or misleading

  2. E-needs-test label: Rust's test suite is enormous but still incomplete. An issue with this label has a bug fixed in the code but no test was added. You can add a test case to capture that fix so it doesn't accidentally break in the future. These tasks usually involve small changes and are very valuable

  3. E-mentor label: This label means a mentor has offered to help with the issue. It's perfect for newcomers because you'll have someone experienced ready to answer your questions as you work on it

  4. E-easy: I don't really want to recommend it because this particular label often attracts a lot of attention and a kind of battle for the issue begins

  5. C-enhancement: could be useful, it's not always about the code and might be less stressfull

My First Contribution Story

Let me digress a bit and share how I made my first contribution to the Rust compiler. One evening I was relaxing with no plans and watching a video by Tsoding about Rust. In it, he tried to access a struct field through a raw pointer using the -> operator. In Rust, you can't do that, that's a C/++ thing, but the compiler still knows what you meant to do. In this example, the error message was actually pretty misleading

So here is what roughly Tsoding was trying to do

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let s = Point { x: 0, y: 2 };

    let p = &s as *const Point;

    p->x;
}

Before my fix, the diagnostic was looks like this

help: `xs` is a raw pointer; try dereferencing it
   |
LL |         (*p)->x;
   |         ++  +

As you can see here, it correctly asks you to dereference it, but keep a -> which still isn't a valid Rust code


Suddenly something clicked in my head: "What's stopping me from start right now and fix that error message?", and I got excited by this idea. At that moment I had literally no open-source experience and didn't even know git well, so I went online to learn what a pull request is and how to make one :)

After some effort, I managed to fork the repository and dive into the code. Then I instantly hit the next roadblock: "There are millions of lines here - how do I find where this error message is generated?" Luckily, luck was on my side. I searched the repository for a unique part of the error text and found a .ftl file - that's a Fluent template for error messages. From there I tracked down the diagnostic struct in the code and finally found where it was being created


I scribbled together a more-or-less working fix and submitted a PR. I still didn't understand a lot of the basics - for example, I didn't know how to re-run the tests with the --bless flag to update expected output. Despite my inexperience, the reviewers (who are amazing people) did most of the heavy lifting, but I loved the experience. It was completely new to me. While that first PR was under review, I already started on a second one: I found another small issue and fixed it. The second PR turned out to be much simpler and got merged even faster than the first

And that's how I stayed involved in the project. I kept finding and fixing interesting problems, all while chatting and collaborating with many really cool people

Here is how that error message looks now after my and reviewers efforts:

help: `p` is a raw pointer; try dereferencing it
   |
11 -     p->x;
11 +     (*p).x;

I

I

I

Thanks for sticking with me through that story! Now, back to the main topic: I want to emphasize something important. Many new contributors (and this is often a mistake) focus on counting merges or commits - chasing those numbers. I believe that's fundamentally the wrong approach and often leads to burnout. I know it sounds weird coming from someone who did 100 merges in the first 6 months just for fun ^^, but honestly, I was just enjoying learning and helping. It was only later that I realized how many that was! Trust me, don't worry about the count - concentrate on the learning and the enjoyment

btw, there's plenty of work you can do without writing a line of code. For example, we already talked about adding tests for E-needs-test issues. Other contributions include triaging issues, improving documentation, or helping to reproduce bugs (E-needs-mvce). These non-code contributions are also very valuable

Small practical step about assigning to issues

Let's say you've found an issue you like and want to work on it. That's great! To show others that you're already working on it, you can use our bot (actually, there's a real person sitting there assigning labels, who knows). - rustbot, or more specifically @rustbot claim to assign yourself and @rustbot release to remove the assignment from yourself (which is totally fine!).

Big practical steps

Once you've claimed an issue, it's time to set up your environment. The first major step is to build the compiler (good page about it). This in average takes 10-15 minutes, usually 5-8 minutes and rarely it's more than 30 minutes, recompiles after small changes is usually faster, so don't worry (also using tip 2 from the end could help you with recompiling time)


If you already have an idea of what you want to work on (maybe you found a bug that interests you or a feature you want to add), it's hard to give one-size-fits-all advice - it really depends on your chosen task

One thing I will strongly encourage is: don't be afraid to ask questions. When maintainers see someone who is genuinely interested and trying to understand, we jump in to help and explain as much as we can

Of course, asking questions out loud can be scary, and it's totally normal to feel shy. Even if you're hesitant, I still recommend live communication: for example, the Rust Zulip chat is great for quick back-and-forth, or you can comment directly on the GitHub issue. If the person who filed the issue is a core team member, they often respond to questions there. Otherwise, someone on the team or the community will likely see your question and help out


Here's a somewhat surprising tip for those who are really stuck: try asking LLM for help. I know, I know - the community is skeptical about language models and no one likes spammy answers/contributions/PRs. I'm a reviewer too, and I know it very well. The key is how you use it

Please don't ask the language model to write a code for you if you don't understand it. Instead, ask specific and concrete questions about your task

For example, "How do I find where this function is defined in the compiler?" or "What does this part of the code do?", language models are pretty good at that. Their suggestions aren't perfect (they usually won't know the quirks of codebase), but they can give you a starting point or point you at the right file. For some people, that little hint is enough to move forward :3


I recently reviewed a pull request that fixed an error message with the assert! and the returned value. In that case, we needed to detect whether the if came from the assert! or not. The author's method wasn't perfect

I decided to conduct my own investigation to suggest a better approach, and I realised that I knew what I was looking for, but I couldn't search for it due to my limited knowledge of regex and grep. So, I described what I was looking for to the LLM, which provided me with some rg commands. I simply copied and pasted them into the terminal and started searching with them

I found what I was looking for! I could also suggest a better, more idiomatic way for the author to rewrite it

When You Get Stuck

Let's say you've opened the code and started digging in, but after a week you still feel stuck and don't know how to proceed. That's a very common feeling - nothing to panic about

If you've hit a wall, I suggest asking for help as we've discussed it above: post a question on Zulip or on the GitHub issue itself (remember, you can mention people, or just write a polite question). Often the issue author or someone else will spot it and give pointers


Another perfectly fine approach is to put the issue aside for now. Maybe you tried to make progress and realized you're blocked or don't have time at the moment

That happens to everyone. If you do put it on hold, leave a note in the issue about what you tried and what the obstacles were

For example: "I explored the code around X and tried using Y, but I'm not sure if that's the right approach. I'm stuck on how to proceed", yes, you didn't fix the issue, but you've saved the next person who picks it up a lot of time - they'll know where you left off and what you've already tried

Your First PR

Now imagine you finally decide to tackle an issue and make your first pull request. You might wonder, "What makes a good PR?", here's the thing: any contribution is welcome. Really! Even if you're just fixing a typo in documentation, adding a test, or cleaning up a bit of code for readability, it's all valuable

I don't like to divide contributions into "good" or "bad." The more important part, imo, is that you did it yourself: you wrote the code and you wrote the PR description. It might be challenging at first, but we want to see your thought process and how you understand the problem. Write a clear summary of what you did in the PR so reviewers can follow along with your reasoning


Personal example: in my first PRs, I often made suboptimal code choices (and honestly, I still do sometimes). At first I didn't know most of the codebase. The reviewers were super helpful and always suggested a more idiomatic or correct approach.

For instance, they might say, "You don't need to implement this yourself - there's already a method doing this", there has never been (and I hope never will be) an occasion where someone yelled at me for writing imperfect code or doing mistakes. We all make mistakes - it's part of learning. Even the core team members make mistakes. The reviewers' job is to help improve the code, not to shame you

Sometimes Even a Well-Crafted PR Gets Closed

Not every contribution ends up merged - and that's okay. Sometimes the code is fine, but the idea behind it isn't what the community wants. Here's a personal example:

I once submitted a PR that aimed to suggest using method syntax x.max(y) when someone wrote max(x, y) but hadn't imported the function. The fix itself was clean and worked correctly:

   else if ["max", "min"].contains(&item_str.as_str())
        && let PathSource::Expr(Some(Expr {
            kind: ExprKind::Call(_, args),
            span: call_span,
            ..
        })) = source
        && args.len() == 2
        && let Some(arg0) = self.r.tcx.sess.source_map().span_to_snippet(args[0].span)
        && let Some(arg1) = self.r.tcx.sess.source_map().span_to_snippet(args[1].span)
    {
        Some((
            *call_span,
            "you may have meant to use the method syntax",
            format!("{arg0}.{item_str}({arg1})"),
        ))
    }

Some team members preferred the free function (std::cmp::max(x, y)) because it's symmetric and feels more natural for a binary operation. The PR wasn't rejected because of bad code - it was a stylistic disagreement

Just keep in mind, that even a "failed" PR is a win if you learn from it. Not every issue needs fixing the way you first think. Sometimes the best contribution is realizing when to step back

Another example of anti-success of mine

I want to share one more personal example that might be useful, especially if you're worried about "breaking something"

Recently, I worked on a fairly large PR that introduced new logic for the MGCA feature. It wasn't a small change: it added quite a bit of new code, went through a long review process, and required several iterations to address feedback. After many discussions and refinements, it finally got merged

The PR was well reviewed, had good test coverage, and didn't cause any regressions in the existing test suite. At that point, everything looked solid

A few days ago, I randomly stumbled upon a new issue. I wasn't pinged, mentioned, or otherwise notified - I just happened to see it while browsing. The issue described a problem that appeared after my PR landed

And this is the important part: this kind of thing is completely normal

Even carefully reviewed changes with good tests can expose new edge cases or interactions that weren't obvious at the time. The compiler is a huge, complex system, and no one can predict all consequences upfront

What matters is not that a bug appeared, but how you react to it

I assigned the issue to myself and left a short note saying that I'd take a look when I had time. Not because I felt guilty, but because this is simply part of the responsibility that comes with contributing. Fixing follow-up issues, investigating regressions, and iterating on previous changes is a normal and expected part of the process

I'm sharing this to make one thing clear: introducing a bug does not make you a bad contributor, but ignoring it may

Dealing with Reviews and CI

So you sent your first pull request - great job, that's a big achievement by itself! After that the CI will automatically start, it will build compiler, check tidy, style, all tests, and maybe something else, it took approx. about hour, so.. be ready

Also, if your CI fails, don't be afraid of it. It's perfectly normal, even for the most skilled top-tier Rust contributors. It's so easy to miss something in the CI checks and cause a failure. Our bot will politely tell you exactly what failed and most likely tell you how to fix it. Just force-push the fix and move on. We all see it daily and fail CI ourselves :3


Now let's talk about what happens during review. You might worry: "Will strict people come and nitpick every little thing in my code, just to embarrass me?" Absolutely not. If the review process seems picky, keep in mind it's not personal. Reviewers are not judging you as a person; they're focused on the code. When someone asks you to fix a bunch of small issues, it's only to improve the project, not to discourage you

As a newcomer, you might not immediately understand what a reviewer is asking for. That's okay. Feel free to ask for clarification. Just comment on the review saying something like "Could you help me understand this comment?" Everyone was new once, and they will help you understand the feedback


Sometimes review comments can sound a bit harsh or blunt. Remember, that's usually just a tone issue - it's much easier to criticize code than to criticize a person. Try to interpret comments as suggestions on the code. If something stings, don't take it personally. You and your contribution matter to us, and we want to see you succeed

Imagine you get a review and they ask you to make a change, but you're not sure what they mean. Perhaps you've been staring at your editor for days, too intimidated to ask, and you feel like you've lost all sense of direction. Or maybe you immediately reach out and ask a reviewer for a clarification on something really simple, and they point out "oh, it was literally 5 lines above your current code." Both situations happen to everyone

The balance is: try solving it yourself first, but not at the cost of burning out. If you spend a reasonable time trying to figure out the requested change and still can't do it, that's okay. Just go back to the reviewer and explain what you tried and where you're stuck. For example: "I tried inserting this line here and running the test, but it still fails with X. I'm not sure how to fix it." This shows you made an effort, and trust me, reviewers deeply appreciate when you explain your attempts. It makes it easier for them to help you and keeps the collaboration going smoothly

Useful resources

Random tips

They are not necesessary, but you may found them useful, they initally were in text, but I decided factor them out to its own chapter

Tip 1

there are compiler flag, that might help you figuring out where error message was created -Ztrack-diagnostics, it will show you a line of code in compiler where error was actually created (it's not always 100% helpful, but very likely would be a good starting point, I found this one very useful at my start)

here is an example

warning: function `is_maybe_transmutable` is never used
--> /home/gh-Kivooeo/test_/src/main.rs:6:12
|
6 |     pub fn is_maybe_transmutable<Src, Dst>()
|            ^^^^^^^^^^^^^^^^^^^^^
|
= note: -Ztrack-diagnostics: created at compiler/rustc_passes/src/dead.rs:1113:18

as you can see above, there is a note with a specific place

Tip 2

Once you have a working build, you can save hours of recompilation time by using the --keep-stage flag. For example

./x b --keep-stage 1

This tells to x to reuse the already-compiled stage 1 compiler when you make changes to later stages. Sometimes you still need a full rebuild, but for most iterative work this flag is a lifesaver

Small conclusion

Starting to contribute to Rust isn't about genius, it's about curiosity and persistence. You don't have to know the entire codebase. You learn, ask questions, and take small steps

That was the first part. I hope it helped you at least a little and dispelled some of your fears

In the next part, I want to take a real issue and walk through the process from start to merge, showing the entire process in detail