필사 모드: Rust Ecosystem 2026 Deep-Dive — Rust 1.85+, tokio, axum, actix-web, sqlx, Bevy, Tauri, Leptos, Dioxus, Embassy, Cargo
EnglishIntro — In May 2026 Rust has hardened into "the systems language standard"
When the rust-for-linux patches were merged into the mainline kernel in late 2024, Rust crossed a line from "promising" to "the language Linux adopted." In May 2026 the shift is even sharper. **Cloudflare's Pingora** (the Rust proxy replacing NGINX inside Cloudflare) handles more than 40 trillion requests per day; **Discord's Read States service** killed GC pauses and hit 99.99% reliability after the Rust rewrite; **Dropbox's Magic Pocket** storage engine, **Figma's multiplayer HTTP layer**, and **Mozilla's Servo** (transferred to the Linux Foundation in 2024) all run on Rust.
This article is not a marketing matrix. It honestly walks through "what does a 2026 production Rust stack actually look like" — from 1.85 / 2024 edition changes, tokio's scheduler, axum/sqlx code, Tauri 2 mobile GA, Bevy 0.15 ECS, Embassy `no_std` async, all the way to Korea/Japan adoption.
The 2026 Rust stack — broken into 8 layers
First, the big picture. The standard 2026 Rust stack divides into 8 layers.
1. **Language / toolchain**: rustc, cargo, rustup, rust-analyzer
2. **Async runtime**: tokio is the de-facto standard; async-std / smol / embassy fill niches
3. **Web / networking**: axum, actix-web 5, poem, rocket 0.5, warp, salvo, loco-rs
4. **Data / ORM**: sqlx, sea-orm, diesel 2.x, rbatis
5. **Errors / observability**: anyhow, thiserror, eyre, snafu, miette, tracing
6. **GUI / rendering**: Tauri 2, egui, Iced, Slint, Dioxus, Yew, Leptos, Sycamore
7. **Systems / embedded / games**: Embassy, RTIC, embedded-hal, Bevy, winit, gtk-rs
8. **Tooling ecosystem**: cargo-nextest, cargo-mutants, cargo-deny, cargo-audit, cross, binstall
The era when a single crate owned each layer is over. "axum + tokio + sqlx + tracing" is now the de-facto backend combo, while GUI splits into "Tauri 2 + Leptos/Dioxus" or "egui/Slint." We walk through every layer below.
Rust 1.85 and the 2024 edition — async fn in trait, let-else, stabilized GATs
Rust 1.85 is the first stable release of the 2024 edition. Key changes:
- **async fn in trait (RPITIT)**: the `async-trait` macro is no longer mandatory. The dyn-safety restriction remains, but for static dispatch you can skip the macro.
- **GAT (Generic Associated Types)**: stabilized in 1.65, with many lints cleaned up in the 2024 edition.
- **let-else**: available since 1.65. The 2024 edition makes temporary lifetimes for `if let` more intuitive.
- **never type fallback**: `!` now falls back to `!` rather than `()`. Some code needs explicit type annotations.
- **raw lifetimes / `r#`**: resolves identifier collisions.
A representative 1.85-style trait definition looks like this.
use std::error::Error;
// 2024 edition: async fn in trait, no macro required
pub trait UserRepo {
async fn find_by_id(&self, id: u64) -> Result<Option<User>, Box<dyn Error + Send + Sync>>;
async fn create(&self, user: NewUser) -> Result<User, Box<dyn Error + Send + Sync>>;
}
pub struct User { pub id: u64, pub email: String }
pub struct NewUser { pub email: String }
pub struct PgUserRepo { pool: sqlx::PgPool }
impl UserRepo for PgUserRepo {
async fn find_by_id(&self, id: u64) -> Result<Option<User>, Box<dyn Error + Send + Sync>> {
let row = sqlx::query_as::<_, (i64, String)>("SELECT id, email FROM users WHERE id = $1")
.bind(id as i64)
.fetch_optional(&self.pool)
.await?;
Ok(row.map(|(id, email)| User { id: id as u64, email }))
}
async fn create(&self, user: NewUser) -> Result<User, Box<dyn Error + Send + Sync>> {
let row: (i64,) = sqlx::query_as("INSERT INTO users(email) VALUES ($1) RETURNING id")
.bind(&user.email)
.fetch_one(&self.pool)
.await?;
Ok(User { id: row.0 as u64, email: user.email })
}
}
tokio runtime internals — work stealing, LIFO slot, cooperative scheduling
tokio uses an M:N work-stealing scheduler. There are three core mechanisms.
- **Local queue + global queue**: each worker owns a local queue (1 LIFO slot + 256 FIFO slots). When it overflows, half of it spills into the global queue.
- **LIFO slot**: the most recently `wake()`d task is polled first, preserving cache locality.
- **Work stealing**: when a worker's queue is empty, it steals half of another worker's queue at random.
Since tokio 1.40 the cooperative scheduling budget defaults to 128. A task that polls 128 times yields automatically — explicit `tokio::task::yield_now().await` in tight loops is almost never needed anymore.
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
#[tokio::main(flavor = "multi_thread", worker_threads = 8)]
async fn main() {
let (tx, mut rx) = mpsc::channel::<u64>(1024);
// 100 producers
for i in 0..100u64 {
let tx = tx.clone();
tokio::spawn(async move {
for j in 0..1000u64 {
let _ = tx.send(i * 1_000 + j).await;
if j % 100 == 0 { sleep(Duration::from_micros(10)).await; }
}
});
}
drop(tx);
// single consumer task
let mut count = 0u64;
while let Some(_v) = rx.recv().await { count += 1; }
println!("received: {count}");
}
`#[tokio::main]` is just sugar; under the hood it calls `tokio::runtime::Builder::new_multi_thread()`. In libraries you typically use the builder directly instead of the macro.
Async runtimes compared — tokio vs async-std vs smol vs embassy
| Runtime | Target | Threading | I/O backend | Note |
| --- | --- | --- | --- | --- |
| tokio | Server / networking standard | multi-thread / current-thread | mio (epoll/kqueue/IOCP) | De-facto standard in 2026 |
| async-std | Simple API-first | multi-thread | smol-based | Maintenance mode; not recommended for new projects |
| smol | Lightweight (~1k LOC) | single / multi | polling / async-io | Embedded, educational |
| embassy | no_std / firmware | cooperative, optionally no-alloc | HAL integration | RP2040 / STM32 / ESP32 |
| glommio | Linux-only io_uring | thread-per-core | io_uring | Databases / storage |
As of May 2026 the standard pattern is "libraries stay runtime-agnostic; applications pick tokio." It is bad manners to bake `#[tokio::main]` into a library crate.
axum — the de-facto async web framework
Built by the tokio team itself, axum went through 0.7 and 0.8 and is now the standard. The defining traits are `tower::Service`-based middleware, a powerful extractor system, and a thin layer over hyper.
use axum::{
Router,
extract::{Path, State},
response::Json,
routing::{get, post},
http::StatusCode,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use sqlx::PgPool;
#[derive(Clone)]
struct AppState { pool: PgPool }
#[derive(Serialize)]
struct User { id: i64, email: String }
#[derive(Deserialize)]
struct CreateUser { email: String }
async fn get_user(
State(state): State<Arc<AppState>>,
Path(id): Path<i64>,
) -> Result<Json<User>, StatusCode> {
let row = sqlx::query_as::<_, (i64, String)>(
"SELECT id, email FROM users WHERE id = $1"
)
.bind(id)
.fetch_optional(&state.pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.ok_or(StatusCode::NOT_FOUND)?;
Ok(Json(User { id: row.0, email: row.1 }))
}
async fn create_user(
State(state): State<Arc<AppState>>,
Json(payload): Json<CreateUser>,
) -> Result<(StatusCode, Json<User>), StatusCode> {
let row: (i64,) = sqlx::query_as("INSERT INTO users(email) VALUES ($1) RETURNING id")
.bind(&payload.email)
.fetch_one(&state.pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok((StatusCode::CREATED, Json(User { id: row.0, email: payload.email })))
}
#[tokio::main]
async fn main() {
let pool = PgPool::connect(&std::env::var("DATABASE_URL").unwrap()).await.unwrap();
let state = Arc::new(AppState { pool });
let app = Router::new()
.route("/users/:id", get(get_user))
.route("/users", post(create_user))
.with_state(state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
Extractors like `Path`, `State`, `Json`, `Query` are all extensible via the `FromRequestParts` or `FromRequest` traits. Middleware composes through `tower::Layer` and plugs in via `Router::layer()`.
actix-web 5 and the other web frameworks — poem, rocket, warp, salvo, loco-rs
Even with axum as the standard, the alternatives are alive and worth knowing.
- **actix-web 5**: the actor model has faded; idiomatic actix-web is now ordinary async-fn handlers. Still at the top of TechEmpower benchmarks. Preferred by teams that micro-tune for throughput.
- **poem**: an extractor design similar to axum, on top of tokio. Standout strength is auto-OpenAPI generation via `poem-openapi`.
- **rocket 0.5**: stabilized on the 1.0 track, but async adoption was slow and new uptake is dropping.
- **warp**: filter combinator design. Maintenance activity is reduced.
- **salvo**: active in the Chinese community. Tree-based router.
- **loco-rs**: "the Rails of Rust." Bundles axum + sea-orm + background jobs + mailer into a full-stack assembly.
| Framework | Defining trait | Recommended scenario |
| --- | --- | --- |
| axum | tower ecosystem, extractors | Default backend choice |
| actix-web 5 | Top-tier throughput | Ultra-high-performance gateways |
| poem | OpenAPI as a first-class citizen | Schema-first APIs |
| loco-rs | Full-stack conventions | "Rails of Rust" fast starts |
| salvo | Tree-based router | China market / very large routing trees |
sqlx, sea-orm, diesel 2.x — comparing the database layer
The DB layer settled into three camps.
- **sqlx 0.8+**: async, compile-time query checking (`query!` macro), Postgres / MySQL / SQLite / MSSQL. Migrations via `sqlx-cli`. The 2026 default.
- **sea-orm**: full ORM, ActiveRecord-style, migration DSL, entity code generation via `sea-orm-cli`. The default ORM for loco-rs.
- **diesel 2.x**: synchronous ORM with `diesel-async` adapter. Strongest compile-time safety, but the macro learning curve is steep.
- **rbatis**: async ORM born in China. Mybatis-style XML / macros.
A sqlx compile-time-checked query example.
use sqlx::PgPool;
#[derive(sqlx::FromRow)]
struct Order { id: i64, user_id: i64, total_cents: i64, status: String }
async fn list_user_orders(pool: &PgPool, user_id: i64) -> sqlx::Result<Vec<Order>> {
// The macro hits the DB at compile time to check SELECT column types.
let orders = sqlx::query_as!(
Order,
r#"SELECT id, user_id, total_cents, status
FROM orders
WHERE user_id = $1
ORDER BY id DESC
LIMIT 100"#,
user_id
)
.fetch_all(pool)
.await?;
Ok(orders)
}
`sqlx::query_as!` looks up the schema either through `DATABASE_URL` or via `sqlx-data.json` (offline mode). Using offline mode in CI is standard practice.
| Crate | Style | Safety | async | Use when |
| --- | --- | --- | --- | --- |
| sqlx | SQL-first + macro checks | Compile-time | Native | Backend default |
| sea-orm | ActiveRecord ORM | Runtime | Native | Full-stack / loco-rs |
| diesel | Typed DSL | Strongest compile-time | Adapter needed | Safety above all |
| rbatis | XML / macro SQL | Runtime | Native | China-market compatibility |
Error handling — anyhow, thiserror, eyre, snafu, miette
The error-handling crates split along two axes.
- **Library error types**: `thiserror` (derive macros), `snafu` (context-adding focus).
- **Application error aggregation**: `anyhow` (absorbs anything into `anyhow::Error`), `eyre` (anyhow + custom reporter), `miette` (pretty diagnostic output).
A typical library/application separation.
// Library side: explicit error types via thiserror
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BillingError {
#[error("invoice not found: {0}")]
NotFound(i64),
#[error("database error")]
Db(#[from] sqlx::Error),
#[error("payment provider error: {0}")]
Provider(String),
}
pub async fn charge(invoice_id: i64) -> Result<(), BillingError> {
Err(BillingError::Provider("stripe timeout".into()))
}
// Application side: accumulate context with anyhow
use anyhow::Context;
pub async fn process_batch(ids: &[i64]) -> anyhow::Result<()> {
for &id in ids {
charge(id).await
.with_context(|| format!("failed to charge invoice {id}"))?;
}
Ok(())
}
`miette` produces compiler-style diagnostics that point at source locations. It is heavily used in user-facing CLI tools.
tracing + tracing-subscriber — the observability standard for async Rust
Past `println!` / `log` / `env_logger`, the 2026 standard for Rust observability is `tracing`. Built by the tokio team, it slots naturally into async code.
use tracing::{info, instrument, error};
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
#[instrument(skip(pool))]
async fn handle_request(pool: &sqlx::PgPool, user_id: i64) -> anyhow::Result<()> {
info!(user_id, "processing request");
let _row = sqlx::query("SELECT 1").fetch_one(pool).await?;
Ok(())
}
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
.with(fmt::layer().json())
.init();
info!("server starting");
}
Adding `tracing-opentelemetry` forwards spans straight into an OTEL tracer; ecosystem exporters like `tracing-loki` and `tracing-bunyan-formatter` are abundant.
serde in 2026 — still the standard, plus miniserde and postcard
`serde` is effectively the standard serialization framework. As of May 2026 the 1.0 line is still maintained; an RFC for "serde 2.0" is under discussion but no stable release yet.
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct ApiResponse {
#[serde(rename = "userId")]
user_id: i64,
#[serde(default)]
tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
}
fn parse_response(s: &str) -> serde_json::Result<ApiResponse> {
serde_json::from_str(s)
}
Alternatives:
- **miniserde**: minimalist serializer that drastically cuts macro compile time. JSON only.
- **postcard**: `serde`-based binary format designed for embedded targets. `no_std` compatible.
- **bincode 2.0**: standard fast binary format. Derive macros are split out.
- **rkyv**: zero-copy deserialization. Performance-first choice.
Tauri 2 — desktop and mobile from the same code
If Tauri 1 was "a lighter Electron alternative," Tauri 2 (GA in 2024, on a stable track by May 2026) bundles desktop + Android + iOS around the same Rust core.
// src-tauri/src/lib.rs
use tauri::command;
#[command]
async fn greet(name: String) -> Result<String, String> {
Ok(format!("Hello, {name}!"))
}
#[command]
async fn read_file(path: String) -> Result<String, String> {
tokio::fs::read_to_string(&path).await.map_err(|e| e.to_string())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet, read_file])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
The plugin system has matured; first-party plugins such as `tauri-plugin-sql`, `tauri-plugin-store`, `tauri-plugin-deep-link`, and `tauri-plugin-notification` cover mobile too. The frontend can be vanilla JS / React / Vue / Svelte / Leptos / Dioxus.
Leptos, Dioxus, Yew, Sycamore — Rust full-stack frontends
WASM + Rust full-stack frameworks split four ways.
- **Leptos 0.7+**: signal-based fine-grained reactivity, first-class SSR / CSR / hydration / `server fn`. Full-stack build via `cargo-leptos`.
- **Dioxus 0.6+**: RSX (React-like) macro, multi-target — desktop / mobile / web / TUI. The Dioxus team turned full-time OSS in 2025.
- **Yew**: classic VDOM. Familiar onboarding but loses to Leptos on full-stack tooling.
- **Sycamore**: Solid-inspired signal-based. Good for learning / small SPAs.
A Leptos full-stack component example.
use leptos::*;
use leptos::server_fn::ServerFn;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
pub struct Todo { pub id: i64, pub title: String, pub done: bool }
#[server]
pub async fn list_todos() -> Result<Vec<Todo>, ServerFnError> {
Ok(vec![Todo { id: 1, title: "ship".into(), done: false }])
}
#[component]
pub fn TodoList() -> impl IntoView {
let todos = create_resource(|| (), |_| async move { list_todos().await });
view! {
{move || todos.get().map(|res| match res {
Ok(items) => view! {
}.into_view(),
Err(_) => view! { <p>"error"</p> }.into_view(),
})}
}
}
The same screen in Dioxus.
use dioxus::prelude::*;
#[derive(Clone, PartialEq, Props)]
struct Todo { id: i64, title: String, done: bool }
fn TodoList(cx: Scope) -> Element {
let todos = use_state(cx, || vec![Todo { id: 1, title: "ship".into(), done: false }]);
cx.render(rsx! {
ul {
todos.iter().map(|t| rsx!{
li { key: "{t.id}", "{t.title}" }
})
}
})
}
| Framework | Paradigm | Multi-target | Full-stack | Use when |
| --- | --- | --- | --- | --- |
| Leptos | Signals, SSR-first | Web | Strong | Default full-stack web |
| Dioxus | RSX, multi-renderer | Web / desktop / mobile / TUI | In progress | Multi-target |
| Yew | VDOM | Web | Weak | Learning / simple SPAs |
| Sycamore | Signals | Web | Weak | Learning |
egui, Iced, Slint — native GUI libraries
If Tauri is "WebView-based," the native GUI side has three options.
- **egui**: immediate-mode GUI. Own renderer (`eframe`), short code, great for in-game UIs and internal tools.
- **Iced**: Elm-inspired, Cmd/Sub pattern. Desktop apps.
- **Slint**: DSL (`.slint` files). Embedded-friendly, with a commercial license available.
use eframe::egui;
struct App { name: String, age: u32 }
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("My egui Application");
ui.horizontal(|ui| {
ui.label("Your name: ");
ui.text_edit_singleline(&mut self.name);
});
ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
if ui.button("Increment").clicked() { self.age += 1; }
ui.label(format!("Hello '{}', age {}", self.name, self.age));
});
}
}
fn main() -> eframe::Result<()> {
let opts = eframe::NativeOptions::default();
eframe::run_native(
"demo",
opts,
Box::new(|_| Ok(Box::new(App { name: "Arthur".into(), age: 42 }))),
)
}
| Library | Paradigm | Renderer | Best for |
| --- | --- | --- | --- |
| egui | Immediate mode | wgpu / glow | In-game debug, internal tools |
| Iced | Elm-like retained | wgpu / tiny-skia | Desktop apps |
| Slint | DSL retained | software / skia / femtovg | Embedded, kiosks |
| Dioxus desktop | WebView | OS WebView | Web-asset reuse |
| Tauri 2 | WebView | OS WebView | Full desktop + mobile |
Bevy 0.15+ — the de-facto Rust game engine
Bevy is a data-oriented ECS game engine. In 0.15 (2025) it landed BSN (Bevy Scene Notation), render graph v2, and a UI overhaul; the 0.16 / 0.17 line in 2026 is adding the editor, hot reload, and more materials.
use bevy::prelude::*;
#[derive(Component)]
struct Player { speed: f32 }
fn spawn_player(mut commands: Commands) {
commands.spawn((
Player { speed: 250.0 },
Sprite::from_color(Color::srgb(0.2, 0.7, 1.0), Vec2::new(40.0, 40.0)),
Transform::default(),
));
}
fn move_player(
time: Res<Time>,
keys: Res<ButtonInput<KeyCode>>,
mut q: Query<(&Player, &mut Transform)>,
) {
for (p, mut t) in &mut q {
let mut dir = Vec3::ZERO;
if keys.pressed(KeyCode::KeyW) { dir.y += 1.0; }
if keys.pressed(KeyCode::KeyS) { dir.y -= 1.0; }
if keys.pressed(KeyCode::KeyA) { dir.x -= 1.0; }
if keys.pressed(KeyCode::KeyD) { dir.x += 1.0; }
t.translation += dir.normalize_or_zero() * p.speed * time.delta_secs();
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, spawn_player)
.add_systems(Update, move_player)
.run();
}
Bevy's charm is that "ECS is a first-class citizen and systems are just functions." Beyond games, it sees growing adoption for simulations and tooling backends.
Embassy, RTIC, embedded-hal — embedded and firmware Rust
In `no_std` embedded land the 2026 story is clear. **Embassy is the de-facto standard**, while interrupt-priority-based deterministic systems still favor **RTIC**.
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_rp::gpio::{Level, Output};
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::task]
async fn blink(mut led: Output<'static>) {
loop {
led.set_high();
Timer::after(Duration::from_millis(500)).await;
led.set_low();
Timer::after(Duration::from_millis(500)).await;
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let led = Output::new(p.PIN_25, Level::Low);
spawner.spawn(blink(led)).unwrap();
}
The `embedded-hal 1.0` release in late 2023 unified the driver ecosystem. Whether you target STM32 / RP2040 / nRF52 / ESP32, the same driver runs on any HAL implementing the `embedded-hal` traits.
CLI and system tools — clap 4.5, argh, ripgrep, bat, fd
For CLI parsers the de-facto standard is `clap`.
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "blogctl", version, about = "Blog management CLI")]
struct Cli {
#[command(subcommand)]
command: Cmd,
}
#[derive(Subcommand)]
enum Cmd {
/// Create a new post
New { title: String, #[arg(short, long)] draft: bool },
/// Publish a post
Publish { slug: String },
}
fn main() {
let cli = Cli::parse();
match cli.command {
Cmd::New { title, draft } => println!("new post: {title} (draft={draft})"),
Cmd::Publish { slug } => println!("publish {slug}"),
}
}
`argh` (Google) keeps derive-macro compile time low and suits small tools. Among system command-line tools, Rust-written ones have become standard: `ripgrep`, `fd`, `bat`, `eza`, `zoxide`, `bottom`, `dust`, `procs`, `hyperfine`, `tokei`, `mdbook`.
Cargo workspaces, build.rs, cross-compilation
Large Rust projects bundle crates into a workspace.
Cargo.toml at the workspace root
[workspace]
resolver = "3"
members = ["crates/api", "crates/domain", "crates/storage"]
[workspace.dependencies]
tokio = { version = "1.40", features = ["full"] }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres"] }
axum = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
anyhow = "1"
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"
For cross-compilation, the `cross` crate is the standard. `cross build --target aarch64-unknown-linux-musl --release` produces a static binary via a Dockerized toolchain.
Cargo tooling — nextest, mutants, deny, audit, machete, expand, binstall
Stock `cargo test` is no longer enough. The 2026 standard companion tools are:
| Tool | Purpose |
| --- | --- |
| cargo-nextest | Parallel test runner, polished output, isolation |
| cargo-mutants | Mutation testing for test-quality verification |
| cargo-deny | License / vulnerability / duplicate-dep checks |
| cargo-audit | RUSTSEC DB vulnerability scanner |
| cargo-machete | Detects unused dependencies |
| cargo-expand | View macro-expanded output |
| cargo-binstall | Install prebuilt binaries |
| cargo-watch | Auto-rebuild on file changes |
| cargo-leptos | Leptos full-stack build |
Standard CI bundle
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo nextest run --workspace --all-features
cargo deny check
cargo audit
cargo machete
WebAssembly — wasm-bindgen, wasm-pack, wasmtime, wasmer
Rust + WASM has two camps.
- **Browser**: `wasm-bindgen` + `wasm-pack`. Automatic bindings between JS and Rust.
- **Server / edge**: `wasmtime` (Bytecode Alliance, sponsored by Fastly / Microsoft) and `wasmer`. WASI Preview 2 settled in 2024, and component-model-based modularization is rolling out.
Cloudflare Workers supports Rust + WASM as a first-class citizen, and companies like Fermyon, Spin, and WasmCloud are pulling WASM-based serverless forward.
rust-for-linux, Cloudflare Pingora, Discord, Dropbox, Figma — production stories
Rust adoption is no longer "the startup experiment."
| Company / project | When | Where Rust is used | Impact |
| --- | --- | --- | --- |
| Linux Kernel | Merged 2022, mainline expansion 2024 | Drivers, Android Binder | Systems-language certification |
| Cloudflare Pingora | Announced 2022, open-sourced 2024 | Next-gen HTTP proxy (NGINX replacement) | More than 40T requests/day |
| Discord | 2020 onward | Read States, gateway | GC pause eliminated, 99.99% reliability |
| Dropbox | 2014 onward | Magic Pocket storage | Exabyte scale |
| Figma | 2022 onward | Multiplayer HTTP layer | Concurrent edit performance |
| Mozilla / Servo | LF transfer 2024 | Next-gen browser engine | Embedded browser |
| AWS | Company-wide | Firecracker, Bottlerocket, parts of S3 | Containers / microVMs |
| Google | Android | Binder, crash subsystem | System memory safety |
| Microsoft | Windows | Selected system components | Memory-safety hardening |
| Meta | Backend | Parts of the service mesh | Officially adopted |
Pitfalls and trade-offs — easy traps when introducing Rust
Five traps we see when teams introduce Rust on a new project.
1. **Compile times**: full builds in a large workspace are long. The standard setup combines `cargo check`-centric workflow, `sccache`, the `mold` linker, and `debug-info=line-tables-only`.
2. **async + Send boundaries**: do not sprinkle `Box<dyn Trait>` everywhere. Library traits should declare `Send + 'static` bounds where realistic.
3. **dyn compatibility vs. RPITIT**: moving every async-trait method to RPITIT breaks dyn-trait usage. Traits used with `dyn` may still need the `async-trait` macro.
4. **Error-type explosion**: do not bake a giant `enum Error` into a single library. Use small per-module error types and lift them with `From`.
5. **unsafe overuse**: 99% of code does not need `unsafe`. If you are not doing FFI / embedded / SIMD, stop and revisit.
Korea and Japan — Rust adoption at Naver, Kakao, CyberAgent, Mercari, Sansan
Korea and Japan are equally active.
- **Naver**: parts of search / ad backends and media pipelines moved to Rust. Internal Rust study groups and seminars run on a recurring cadence.
- **Kakao Brain / Kakao Enterprise**: Rust for ML inference servers and model gateways, combined with Python via PyO3.
- **Discord Korea / Twitch Korea group**: relies on the global Rust stack as-is.
- **CyberAgent**: rewrote parts of ad and game infrastructure in Rust, including some AbemaTV microservices.
- **Mercari**: search, infra tools, and CLIs in Rust. Rust roles open continuously.
- **Sansan**: Rust in the business-card OCR post-processing pipeline.
- **LINE Yahoo**: system daemons and build tools migrated to Rust.
Both Tokyo and Seoul host quarterly Rust meetups. Korea has the "Rust Korea" Slack / Discord / Facebook groups; Japan has the recurring "Rust.Tokyo" conference.
A 2026 learning roadmap — what to study, in what order
A recommended flow for developers picking up Rust today.
1. **The Rust Programming Language ("the book")** chapters 1-10: build intuition for ownership, borrowing, and lifetimes.
2. **rustlings**: small fill-in-the-blank exercises to get comfortable with compiler messages.
3. **`cargo new` a CLI**: clap + serde + anyhow combo.
4. **A small API with tokio + axum**: sqlx + Postgres + tracing all the way through.
5. Study the **2024 edition changes and async fn in trait** explicitly.
6. Pick a domain:
- Web / full-stack: Leptos or Dioxus.
- Desktop: Tauri 2 or egui.
- Games: Bevy.
- Embedded: Embassy + RP2040 or STM32 Discovery.
- Systems: read the source of rust-for-linux, Firecracker, or Pingora.
The goal is not "write fast" — it is "talk with the compiler and build a model." Compiler errors are friends.
Wrap-up — in 2026 Rust is not "a choice" but "a default"
Connect the dots — rust-for-linux merged in 2024, Tauri 2 GA in 2024, Pingora open-sourced in 2025, axum 0.8 in 2025, Rust 1.85 / 2024 edition in 2026 — and the picture is clear. Backend defaults to tokio + axum + sqlx; desktop / mobile defaults to Tauri 2; games default to Bevy; embedded defaults to Embassy; full-stack web defaults to Leptos / Dioxus.
The question is no longer "should we use Rust?" but "where do we use Rust?" — and the answer keeps expanding.
References
- Rust official — https://www.rust-lang.org/
- The Rust Book — https://doc.rust-lang.org/book/
- Rust blog — https://blog.rust-lang.org/
- tokio — https://tokio.rs/
- axum — https://github.com/tokio-rs/axum
- actix-web — https://actix.rs/
- sqlx — https://github.com/launchbadge/sqlx
- Diesel — https://diesel.rs/
- SeaORM — https://www.sea-ql.org/SeaORM/
- Bevy — https://bevyengine.org/
- Tauri — https://tauri.app/
- Leptos — https://leptos.dev/
- Dioxus — https://dioxuslabs.com/
- Yew — https://yew.rs/
- Embassy — https://embassy.dev/
- crates.io — https://crates.io/
- rust-for-linux — https://rust-for-linux.com/
- Cloudflare Pingora — https://github.com/cloudflare/pingora
- Servo (Linux Foundation) — https://servo.org/
- Discord Rust case study — https://discord.com/blog/why-discord-is-switching-from-go-to-rust
현재 단락 (1/494)
When the rust-for-linux patches were merged into the mainline kernel in late 2024, Rust crossed a li...