The projects Rust hiring managers actually look for: not tutorial clones, but real systems that demonstrate ownership model mastery, async proficiency, and production thinking.
By Max Wells, updated July 2026
TL;DR: Rust hiring managers don't want to see another todo list or "hello world" HTTP server. They want evidence of production-level thinking: proper error handling, async runtime understanding, memory management decisions, and the ability to design maintainable APIs.
- Project 1: A CLI tool with real functionality: demonstrates ownership basics and binary distribution
- Project 2: A REST API with authentication and database: demonstrates async Rust and production patterns
- Project 3: A concurrent data processor: demonstrates channels, Rayon, and performance thinking
- Project 4: A custom protocol implementation or systems tool: demonstrates deep Rust knowledge
- Project 5: An open-source contribution: demonstrates collaboration and community engagement
- Bonus: An AI/LLM tool using Rust: extremely valued in 2026
Who Should Read This?
This article is for developers who are learning Rust and want to build a portfolio that leads to actual job offers: not just technical knowledge for its own sake. It is written for career switchers, bootcamp graduates, and self-taught programmers who are targeting Rust engineering roles. Senior Rust engineers in the United States earn $170K–$230K at infrastructure startups and $185K–$260K in total comp at larger companies. The bottleneck for most candidates is not knowledge: it is evidence. This guide tells you exactly what to build to provide that evidence, and why each project signals what hiring managers are looking for.
What Do Rust Hiring Managers Actually Look For in a Portfolio?
Hiring managers look for evidence that you understand Rust's ownership model deeply enough to make decisions: not just write code that compiles.
The most common portfolio mistake: too many tutorial-following projects (building the same REST API five different ways) and not enough projects that demonstrate judgment.
What signals seniority in a Rust portfolio:
| Signal | What it shows |
|---|---|
Proper error handling with thiserror/anyhow | You've thought about error propagation, not just unwrap() everywhere |
| Lifetime annotations in structs | You've worked past surface-level ownership |
| Async code with real concurrency | You understand Send bounds, not just async/await syntax |
| Tests that actually test something | You write testable code, not just code that runs |
| A README explaining design decisions | You can communicate technical choices |
| Performance benchmarks or profiling | You've measured, not just assumed |
The bar is not perfection. A junior-level hire with two solid projects beats a candidate with ten toy projects. Senior-level hires are expected to have either open-source contributions or a project that demonstrates systems-level thinking.
Bottom line: Two deep projects outperform ten shallow ones. Pick two projects from this list, finish them completely, add tests and a real README, then spend the rest of your time applying: not building more projects.
Project 1: A Real CLI Tool (Skill Level: Junior)?
A CLI tool is the best first Rust portfolio project: it exercises ownership, error handling, file I/O, and binary distribution without requiring async complexity.
What to build: Don't build a todo list. Build something you would actually use:
- A log analyzer: parse structured logs, filter by severity/time range, output summaries
- A file deduplicator: find duplicate files by hash, print a report, optionally delete
- A code statistics tool: count lines, functions, and complexity metrics for a codebase
- A JSON/CSV transformer: convert between formats with a flexible mapping config
Example: log-grep: a faster, colored version of grep for structured logs
use clap::Parser;
use std::io::{BufRead, BufReader};
use std::fs::File;
#[derive(Parser)]
#[command(name = "log-grep")]
#[command(about = "Filter and colorize structured log files")]
struct Args {
#[arg(help = "Log file to search")]
file: String,
#[arg(short, long, help = "Filter by log level (ERROR, WARN, INFO)")]
level: Option<String>,
#[arg(short, long, help = "Filter by string pattern")]
pattern: Option<String>,
#[arg(short = 'n', long, default_value = "0", help = "Show last N lines (0 = all)")]
tail: usize,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let file = File::open(&args.file)
.map_err(|e| anyhow::anyhow!("Could not open '{}': {}", args.file, e))?;
let reader = BufReader::new(file);
let mut lines: Vec<String> = Vec::new();
for line in reader.lines() {
let line = line?;
// Apply filters
if let Some(ref level) = args.level {
if !line.contains(level.as_str()) { continue; }
}
if let Some(ref pattern) = args.pattern {
if !line.contains(pattern.as_str()) { continue; }
}
lines.push(line);
}
// Apply tail
let output = if args.tail > 0 && lines.len() > args.tail {
&lines[lines.len() - args.tail..]
} else {
&lines[..]
};
for line in output {
println!("{}", colorize(line));
}
Ok(())
}What makes this portfolio-worthy:
- Uses
anyhowfor ergonomic error propagation - Streams file content: doesn't load the whole file into memory
- Has meaningful CLI flags with
clap - Compiles to a self-contained binary
How to extend it: Add --json output, benchmark against grep with criterion, add regex support, publish to crates.io.
Project 2: A REST API With Auth and a Real Database (Skill Level: Junior–Mid)?
A production-style REST API demonstrates async Rust, database integration, authentication, error handling, and the full stack that most backend Rust roles require.
What to build: Not just a user CRUD. Build something with a business domain:
- A bookmark manager API (users, tags, URLs, full-text search)
- A budget tracker (transactions, categories, monthly summaries)
- A job application tracker (companies, positions, interviews, status)
Stack: Axum + SQLx + PostgreSQL + JWT authentication + Docker
What hiring managers look for in this project:
// ✅ Typed error handling: not unwrap()
#[derive(Debug, thiserror::Error)]
enum ApiError {
#[error("Not found")]
NotFound,
#[error("Unauthorized")]
Unauthorized,
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
}
impl IntoResponse for ApiError { /* ... */ }
// ✅ Proper state injection
#[derive(Clone)]
struct AppState {
db: PgPool,
jwt_secret: String,
}
// ✅ Input validation
#[derive(Deserialize, Validate)]
struct CreateBookmark {
#[validate(url)]
url: String,
#[validate(length(min = 1, max = 200))]
title: String,
}
// ✅ Clean handler signatures
async fn create_bookmark(
State(state): State<AppState>,
AuthUser(user_id): AuthUser,
Json(input): Json<CreateBookmark>,
) -> Result<Json<Bookmark>, ApiError> {
input.validate()?;
let bookmark = db::create_bookmark(&state.db, user_id, input).await?;
Ok(Json(bookmark))
}Must-haves for the project to be portfolio-worthy:
- JWT authentication middleware
- Input validation (not just trusting user input)
- Integration tests that spin up a real test database
- Docker Compose so reviewers can run it locally
- OpenAPI docs (via
utoipaor similar)
Project 3: A Concurrent Data Processor (Skill Level: Mid)?
A data processing pipeline that handles large files concurrently demonstrates your understanding of Rust's concurrency model: channels, Rayon, and memory efficiency under load.
What to build:
- A CSV pipeline: read a large CSV (100M+ rows), transform, aggregate, output
- An image batch processor: resize/optimize/watermark thousands of images in parallel
- A log aggregator: parse, deduplicate, and summarize logs from multiple services
- A web crawler: fetch URLs concurrently with rate limiting and politeness
Key patterns to showcase:
use rayon::prelude::*;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
// Rayon for CPU-bound parallel work
fn count_words_parallel(files: Vec<PathBuf>) -> HashMap<String, u64> {
files
.par_iter() // parallel iterator from Rayon
.map(|path| {
let content = std::fs::read_to_string(path).unwrap_or_default();
count_words(&content)
})
.reduce(HashMap::new, |mut acc, map| {
for (word, count) in map {
*acc.entry(word).or_insert(0) += count;
}
acc
})
}
// Tokio channels for async producer-consumer pipelines
async fn process_pipeline(input_rx: mpsc::Receiver<Record>) {
let (processed_tx, mut processed_rx) = mpsc::channel::<ProcessedRecord>(1000);
// Spawn a processor task
tokio::spawn(async move {
while let Some(record) = input_rx.recv().await {
let processed = transform(record).await;
processed_tx.send(processed).await.ok();
}
});
// Write output
while let Some(record) = processed_rx.recv().await {
write_output(record).await;
}
}What makes this impressive to hiring managers:
- Demonstrates you can reason about throughput and bottlenecks
- Shows understanding of when to use
Rayon(CPU-bound) vsTokio(I/O-bound) - Includes benchmarks showing how throughput scales with thread count
- Handles backpressure (bounded channels, not unbounded)
Project 4: A Systems-Level Tool (Skill Level: Mid–Senior)?
A systems tool: a proxy, a database, a network protocol implementation, or a runtime: is the highest-signal portfolio project for senior Rust roles.
What to build (pick one):
A key-value store: TCP server accepting GET/SET/DEL commands, persisted to disk, with a WAL (Write-Ahead Log) for crash recovery. Shows: async networking, file I/O, serialization, and thinking about durability guarantees.
A mini HTTP/1.1 server from scratch: Parse raw TCP bytes, implement request/response parsing, serve static files. Shows: protocol implementation, zero-copy buffer management, and performance optimization.
A DNS resolver: Forward DNS queries to upstream servers with caching. Shows: UDP, binary protocol parsing, caching strategies, and TTL management.
A rate limiter library: A token bucket or sliding window rate limiter with Redis or in-memory storage. Shows: concurrent data structure design, atomic operations, and API design for library authors.
// Example: Key-value store core
use tokio::net::{TcpListener, TcpStream};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
type Db = Arc<RwLock<HashMap<String, Vec<u8>>>>;
async fn handle_connection(mut stream: TcpStream, db: Db) {
let mut buf = [0u8; 4096];
loop {
let n = stream.read(&mut buf).await.unwrap_or(0);
if n == 0 { return; }
let command = parse_command(&buf[..n]);
let response = match command {
Command::Get(key) => {
let db = db.read().await;
match db.get(&key) {
Some(val) => format!("+{}\r\n", String::from_utf8_lossy(val)),
None => "$-1\r\n".to_string(),
}
},
Command::Set(key, value) => {
db.write().await.insert(key, value);
"+OK\r\n".to_string()
},
Command::Del(key) => {
let removed = db.write().await.remove(&key).is_some();
format!(":{}\r\n", if removed { 1 } else { 0 })
},
};
stream.write_all(response.as_bytes()).await.ok();
}
}Project 5: An Open-Source Contribution (Any Level)?
Contributing to an existing Rust project is the most credible portfolio signal: it shows you can read production code, navigate an unfamiliar codebase, and collaborate with other engineers.
How to find a good first contribution:
- Look for
good-first-issuelabels on:tokio,axum,serde,clap,sqlx,reqwest,rustfmt - Fix a real bug you encountered while using a crate
- Add a missing test case
- Improve documentation with an example you wished existed
- Implement a small, clearly-scoped feature from an open issue
What makes a contribution impressive:
- The PR was merged (not just submitted)
- The PR touched non-trivial code (not just a typo fix)
- You can explain the change and the tradeoffs you considered
Even a single merged PR in a crate with 5K+ stars is more impressive than five tutorial portfolio projects. It proves you can navigate real-world Rust codebases.
Bottom line: One merged PR in Axum, Tokio, or serde signals to Cloudflare or Discord hiring managers that you can operate in production codebases: no cover letter required.
What Common Mistakes Do Rust Portfolio Builders Make When Job Hunting?
Portfolio mistakes reveal candidates who understand Rust syntax but haven't yet thought about production engineering: each mistake has a clear, specific fix.
-
Building five small projects instead of two strong ones. Quality signals significantly more than quantity in a Rust portfolio. A hiring manager who opens three repositories and finds shallow code in all three will conclude you cannot sustain depth. Focus your effort: build one genuinely solid project at each of your target skill levels and invest the remaining time in extending and polishing those two.
-
Using
unwrap()liberally in portfolio code. In tutorials and learning exercises,unwrap()is fine. In portfolio code that hiring managers will read, it signals that you have not yet internalized Rust's error handling model. Replace everyunwrap()in non-test code with proper?,map_err, orif let Ok(...)handling. A single well-typed error enum that propagates through your application is more impressive than a dozenexpect("should never fail")calls. -
No tests. Portfolio projects with zero tests suggest the candidate does not write testable code. Add at minimum: unit tests for your core logic, one integration test that exercises a real code path from top to bottom, and (for APIs) at least one test using a real database. Use
tokio::testfor async tests and keep tests inmod unit_testsandmod integration_testsblocks for clarity. -
No README explaining design decisions. The README is where you demonstrate your ability to communicate technical choices: which hiring managers care about as much as the code itself. A strong README includes: what problem the project solves, what technical decisions you made and why (e.g., "I chose SQLx over Diesel because the project is async-first"), what you would do differently if starting over, and how to run the project locally. This converts a code repository into a portfolio artifact.
-
Building in a domain the target company doesn't care about. If you are targeting a vector database company, build a project that involves data indexing or nearest-neighbor search. If you are targeting a networking company, build something with async TCP. Tailoring your visible projects to the companies you want to work at is not dishonest: it is evidence that you understand the domain. Generic portfolio projects, however technically sound, do not demonstrate domain enthusiasm.
-
Not deploying or distributing the project. A CLI tool published to crates.io, an API running on a public URL, or a WASM demo accessible in the browser turns a code repository into a product. It signals that you have thought about the full lifecycle of software: not just writing it. Publishing to crates.io is particularly impactful: it means your code was clean enough and documented well enough to ship.
Bottom line: A deployed project (crates.io, public API, or WASM demo) signals that you ship: not just code. Hiring managers at Cloudflare and Discord specifically look for candidates who treat portfolio projects like real products.
A Structured Path to Portfolio and Interview Readiness
If you want a structured plan for building a portfolio that specifically targets Rust engineering roles: with project templates, code review checkpoints, and mock technical interviews: Rustify's 9-week bootcamp provides exactly that, with 1:1 coaching from engineers who have hired for Rust roles. The bootcamp is designed to take developers from "comfortable with Rust basics" to "ready to interview at Rust-first companies" in nine weeks with a cohort format and weekly deliverables.
Keep Reading
- Rust Developer Salary in the USA (2026)
- How Long to Learn Rust by Background: Hours, Timeline & ROI
- Best Way to Learn Rust in 2026
- Is Rust Hard to Learn?
Frequently Asked Questions
Two strong projects are enough. One CLI tool (Project 1) and one REST API (Project 2) cover the basics. The quality matters far more than the quantity. Ensure both have tests, a README explaining design decisions, and working Docker setup. Apply to junior roles after you can explain every line of code in both projects clearly: not before. The interview is when you are expected to walk through the code in detail.
Only if you have extended them significantly beyond the tutorial. A verbatim Rustlings completion or a copied Axum "hello world" adds noise without signal. Show only projects where you made real decisions. If you want to demonstrate that you completed Rustlings or a similar exercise, mention it in your resume's education section: do not link to the repository unless it contains your own extensions.
Add benchmarks with Criterion to your CLI tool or data processor. Even simple "this is 3x faster than the naive implementation" benchmarks show that you think about performance. Profile one hot path and document what you found: what tool you used (flamegraph, perf, cargo-flamegraph), what you saw, and what change you made. The benchmark and the explanation matter more than the absolute numbers.
Yes: especially for roles at browser-adjacent companies. Building a small WASM module that runs in the browser (a text processor, a game, a data visualizer) shows competency in a domain with growing demand. Pair it with a TypeScript wrapper using wasm-bindgen to show the full integration story. WASM Rust roles are concentrated at companies building in the developer tools, web, and edge computing space.
Open the PR, walk through the change, and explain: "This was the issue, here's why the existing code had a problem, here's my solution, and here's the alternative I considered and rejected." The ability to explain tradeoffs is what interviewers are listening for. Prepare to answer follow-up questions about the crate's broader architecture: you should have read enough of the codebase to answer these comfortably.
Entry-level Rust roles with a strong portfolio (two solid projects + one open-source contribution) typically pay $120K–$145K at US-based infrastructure companies and £85K–£120K in London. Mid-level roles with a systems-level project (Project 4) and domain specialization: fintech, embedded, or AI infrastructure: pay $155K–$185K in the US and £110K–£145K in London. The portfolio does not just get you the interview: it anchors the salary conversation: a candidate who can walk through a production-grade Rust project has significantly more leverage than one who claims Rust familiarity on a resume.
