Rust 2026

Rust 2026 기본기 정리

Edition 2024 기준 최신 문법과 표준 라이브러리, 비동기, 동시성, 패턴까지 한 페이지로 모은 입문~중급 핵심 노트.
2026-05 기준 stable Rust(1.87+)에서 동작하는 코드만 수록.

1. 왜 Rust 2026인가

2. 설치 & 툴체인

# macOS / Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 버전 / 업데이트
rustup update
rustc --version          # rustc 1.87.x (stable)
cargo --version

# 컴포넌트
rustup component add clippy rustfmt rust-analyzer rust-src

# 툴체인 핀(프로젝트 루트)
echo '[toolchain]\nchannel = "1.87.0"\ncomponents = ["rustfmt","clippy"]' > rust-toolchain.toml
도구역할
rustup툴체인 매니저 (stable/beta/nightly, 컴포넌트, 타깃)
cargo빌드/패키지/테스트/배포 통합 CLI
rustc실제 컴파일러
rustfmt포매터 (cargo fmt)
clippy린터 (cargo clippy -- -D warnings)
rust-analyzerLSP — VS Code/Zed/Helix에 필수

3. Cargo & 프로젝트 구조

cargo new hello              # 바이너리
cargo new mylib --lib        # 라이브러리
cargo run
cargo build --release
cargo test
cargo check                  # 타입체크만 (빠름)
cargo add tokio --features full
cargo remove anyhow

Cargo.toml (Edition 2024)

[package]
name = "myapp"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"

[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"
thiserror = "2"

[profile.release]
lto = "fat"
codegen-units = 1
strip = "symbols"

워크스페이스

[workspace]
members = ["crates/*"]
resolver = "3"          # Edition 2024 기본값

[workspace.package]
edition = "2024"
rust-version = "1.85"

[workspace.dependencies]
serde = "1"           # 멤버에서 serde.workspace = true

4. 기본 문법

변수 · 가변성 · 섀도잉

let x = 5;              // 불변 (기본)
let mut y = 5;          // 가변
let x: i32 = 10;        // 섀도잉 — 같은 이름 재선언, 타입 변경 가능
const MAX: u32 = 100;   // 컴파일타임 상수, 항상 SCREAMING
static NAME: &str = "rinda"; // 정적 (수명 'static)

기본 타입

분류타입
정수i8/i16/i32/i64/i128/isize, u8/...usize
실수f32, f64
불리언/문자bool, char (4바이트 유니코드 스칼라)
문자열&str (뷰), String (소유)
복합튜플 (T1, T2), 배열 [T; N], 슬라이스 &[T], Vec<T>
유닛() — "값 없음"

함수 · 표현식

fn add(a: i32, b: i32) -> i32 {
    a + b                  // 마지막 표현식이 반환값(세미콜론 X)
}

fn classify(n: i32) -> &'static str {
    if n > 0 { "양수" }       // if도 표현식
    else if n < 0 { "음수" }
    else { "영" }
}

제어 흐름

// loop / break with value
let answer = loop {
    break 42;
};

// while let
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
    println!("{top}");
}

// for + range
for i in 0..5 { println!("{i}"); }
for (i, v) in ["a", "b"].iter().enumerate() { … }

// let-else (1.65+) — 조기 반환에 최적
let Some(name) = lookup() else { return Err(...); };

// let-chains (Edition 2024 stable)
if let Some(u) = user && u.active && let Some(p) = u.plan {
    println!("{}", p.tier);
}

5. 소유권 · 빌림 · 수명 ★ 핵심

Rust의 정체성. 컴파일러가 "메모리를 누가 책임지나"를 추적해 GC 없이 안전을 보장한다.

3대 규칙

  1. 모든 값은 정확히 하나의 소유자(owner)를 가진다.
  2. 소유자가 스코프를 벗어나면 값은 즉시 drop된다.
  3. 참조(&T)는 동시에 여러 개의 불변 참조 또는 단 하나의 가변 참조만 허용된다.
let s1 = String::from("hi");
let s2 = s1;              // 소유권 이동(move). 이후 s1 사용 불가.
// println!("{s1}");      // ← 컴파일 에러

let s3 = s2.clone();      // 깊은 복제. 소유권 두 개로 분리.

fn len(s: &String) -> usize { s.len() }   // 빌림 — 소유권 안 가져감
fn push_x(s: &mut String) { s.push('x'); }   // 가변 빌림

수명(lifetime)

// 입력 두 참조 중 더 짧은 수명을 반환에 매칭
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
대부분의 수명은 생략(elision)된다
  • 입력 참조 1개 → 모든 출력에 같은 수명.
  • 메서드(&self) → 출력 수명 = &self.
  • 그 외엔 명시. 컴파일러 메시지가 정확히 알려준다.

Copy vs Move

i32·bool·char·고정 크기 배열 등 Copy 트레이트 구현 타입은 대입 시 복제된다. String·Vec·Box는 이동.

6. 구조체 · 열거형 · 패턴 매칭

구조체

#[derive(Debug, Clone, PartialEq)]
struct User { id: u64, name: String, active: bool }

impl User {
    fn new(id: u64, name: impl Into<String>) -> Self {
        Self { id, name: name.into(), active: true }
    }
    fn deactivate(&mut self) { self.active = false; }
}

// 튜플 구조체 / 유닛 구조체
struct UserId(u64);
struct Marker;

열거형 (대수적 데이터 타입)

enum Shape {
    Circle { radius: f64 },
    Rect(f64, f64),
    None,
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Circle { radius } => 3.14159 * radius * radius,
            Shape::Rect(w, h)         => w * h,
            Shape::None               => 0.0,
        }
    }
}

패턴 매칭

match n {
    0           => "zero",
    1..=9      => "single digit",
    x if x < 0  => "negative",
    _           => "big",
}

// 구조 분해
let User { name, .. } = user;
let (a, b, c) = (1, 2, 3);

// or 패턴 / @ 바인딩
match n {
    n @ 1 | n @ 3 | n @ 5 => println!("odd small {n}"),
    _ => {}
}

Option / Result

let some_n: Option<i32> = Some(5);
let got = some_n.unwrap_or(0);
let doubled = some_n.map(|x| x * 2);

fn parse(s: &str) -> Result<i32, std::num::ParseIntError> {
    s.parse::<i32>()           // 그대로 반환
}

fn double(s: &str) -> Result<i32, _> {
    Ok(parse(s)? * 2)         // `?` — 실패 시 조기 반환
}

7. 트레이트 · 제네릭

트레이트 정의 / 구현

trait Greet {
    fn hello(&self) -> String;
    fn shout(&self) -> String {       // 기본 구현
        format!("{}!!", self.hello())
    }
}

impl Greet for User {
    fn hello(&self) -> String { format!("hi, {}", self.name) }
}

제네릭 + 트레이트 바운드

// where 절로 가독성
fn sum<T>(items: &[T]) -> T
where
    T: Copy + std::ops::Add<Output = T> + Default,
{
    let mut acc = T::default();
    for &x in items { acc = acc + x; }
    acc
}

// `impl Trait` — 익명 반환 타입
fn make_iter() -> impl Iterator<Item = u32> {
    (0..).filter(|n| n % 3 == 0)
}

정적 디스패치 vs 동적 디스패치

정적 (제네릭/impl Trait)동적 (dyn Trait)
비용0 (모노모피화)vtable 1단계 인다이렉션
이종 컬렉션불가가능 (Vec<Box<dyn T>>)
코드 크기커짐작음

RPITIT / AFIT 2024+

// trait 메서드가 impl Trait을 반환 (Edition 2024 stable)
trait Repository {
    fn all(&self) -> impl Iterator<Item = User>;

    // async fn in trait — 동적 디스패치 시엔 trait 객체 wrapper 필요
    async fn find(&self, id: u64) -> Option<User>;
}

8. 에러 처리

표준 패턴

thiserror — 라이브러리 에러 정의

use thiserror::Error;

#[derive(Debug, Error)]
pub enum DbError {
    #[error("connection failed: {0}")]
    Conn(#[from] std::io::Error),

    #[error("row {id} not found")]
    NotFound { id: u64 },

    #[error("deserialize: {0}")]
    De(#[from] serde_json::Error),
}

anyhow — 애플리케이션 에러

use anyhow::{Context, Result};

fn load(path: &str) -> Result<Config> {
    let data = std::fs::read_to_string(path)
        .with_context(|| format!("reading {path}"))?;
    let cfg = serde_json::from_str(&data)
        .context("parse config")?;
    Ok(cfg)
}
언제 anyhow vs thiserror? 라이브러리(공개 API)에서는 호출자가 분기할 수 있도록 thiserror로 enum. 애플리케이션(바이너리)에서는 컨텍스트 체인으로 충분하니 anyhow.

9. 컬렉션 · 이터레이터

타입용도핵심 API
Vec<T>가변 배열push/pop/iter/extend
StringUTF-8 가변 문자열push_str/format!/chars
HashMap<K,V>해시 맵insert/get/entry
BTreeMap정렬 맵범위 쿼리
HashSet중복 없는 집합insert/contains
VecDeque양방향 큐push_front/back

이터레이터 — Rust의 정수

let nums = vec![1, 2, 3, 4, 5];

let sum_of_squares: i32 = nums.iter()
    .filter(|&&x| x % 2 == 1)
    .map(|&x| x * x)
    .sum();                    // 9 + 1 + 25 = 35

let grouped: HashMap<bool, Vec<i32>> = nums.into_iter()
    .fold(HashMap::new(), |mut acc, n| {
        acc.entry(n % 2 == 0).or_default().push(n);
        acc
    });

3대 변환 메서드

메서드의미
iter()불변 참조 &T
iter_mut()가변 참조 &mut T
into_iter()소유권 이동 T

gen 블록 Edition 2024

// 게으른 이터레이터를 yield 문법으로 작성
fn fib() -> impl Iterator<Item = u64> {
    gen {
        let (mut a, mut b) = (0u64, 1);
        loop {
            yield a;
            (a, b) = (b, a + b);
        }
    }
}

10. 클로저 & Fn 트레이트

let y = 10;
let add_y = |x| x + y;       // 캡처: y 빌림
let moved   = move |x: i32| x + y; // y 소유권 이동
트레이트호출 방식특성
Fn여러 번 호출, 환경을 빌림가장 제약 적음
FnMut여러 번 호출, 환경을 가변 빌림상태 변경
FnOnce한 번만 호출, 환경을 소비가장 강력

async closure 1.85+

async fn retry<F>(mut f: F) -> Result<()>
where F: AsyncFnMut() -> Result<()>,
{
    for _ in 0..3 {
        if f().await.is_ok() { return Ok(()); }
    }
    Err(anyhow!("3 attempts failed"))
}

11. 모듈 · 크레이트 · 가시성

// src/lib.rs
pub mod users;             // src/users.rs 또는 src/users/mod.rs
pub mod billing { pub mod tier; }

// 가시성 단계
pub                        // 어디서나
pub(crate)                 // 같은 크레이트만
pub(super)                 // 부모 모듈만
pub(in path)               // 특정 경로만
// (지정 없음)             // 같은 모듈만 (private 기본)

// use 별칭
use std::collections::HashMap as Map;
use crate::users::{User, UserId};

12. 스마트 포인터

타입의미언제
Box<T>힙 할당, 단일 소유재귀 타입, trait 객체, 큰 값
Rc<T>참조 카운팅 (싱글스레드)그래프, 공유 소유
Arc<T>원자적 RC (스레드 안전)스레드 간 공유
Cell/RefCell내부 가변성 (싱글스레드)불변 외형, 가변 내부
Mutex/RwLock동기 잠금스레드 공유 가변
Cow<T>Clone-on-Write대부분 빌림, 가끔 소유
use std::sync::Arc;
use tokio::sync::Mutex;       // async에선 tokio::sync 사용

let shared = Arc::new(Mutex::new(vec![0; 10]));
let shared2 = Arc::clone(&shared);  // 카운트 +1

13. 동시성

스레드

use std::thread;
use std::sync::mpsc;

let (tx, rx) = mpsc::channel();

for i in 0..4 {
    let tx = tx.clone();
    thread::spawn(move || {
        tx.send(i * i).unwrap();
    });
}
drop(tx);
for v in rx { println!("{v}"); }

scoped threads 1.63+

let data = vec![1, 2, 3];
thread::scope(|s| {
    s.spawn(|| println!("{:?}", &data));   // 빌림 OK
    s.spawn(|| println!("len={}", data.len()));
});  // 모든 스레드 join 후 반환

Send / Sync

14. async / await

최소 예제 (tokio)

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let body = reqwest::get("https://example.com")
        .await?
        .text().await?;
    println!("{} bytes", body.len());
    Ok(())
}

spawn / join / select

use tokio::{join, select, time::{sleep, Duration}};

let a = tokio::spawn(async { 1 });
let b = tokio::spawn(async { 2 });
let (ra, rb) = join!(a, b);

select! {
    _ = sleep(Duration::from_secs(5)) => println!("timeout"),
    msg = rx.recv() => println!("got {msg:?}"),
}

async fn in trait 2024 stable

trait Mailer {
    async fn send(&self, to: &str, body: &str) -> Result<()>;
}

struct SmtpMailer;
impl Mailer for SmtpMailer {
    async fn send(&self, to: &str, body: &str) -> Result<()> { … }
}
주의 — std::sync::Mutex를 await 하지 마라 async 컨텍스트에서 lock을 들고 있는 동안 .await 하면 worker thread가 점유된다. 그 경우엔 tokio::sync::Mutex(또는 RwLock) 사용.

15. 2024 Edition · 2026 신문법 모음

기능요지버전
let-else단일 패턴 + else 조기 반환1.65
GAT제네릭 연관 타입1.65
scoped threadsborrow 가능한 스레드 풀1.63
RPITIT / AFITtrait이 impl Trait · async fn 반환1.75
async closure / AsyncFn*고차 async 함수 정의1.85
let-chainsif let … && … 합성2024 ed.
gen 블록이터레이터를 yield 문법으로2024 ed.
RPIT 캡처 단순화impl Trait + use<…> 명시 캡처2024 ed.
tail call 보장 become실험적, 일부 nightly
const generics 확장표현식 일반화 진행 중

RPIT 캡처 명시 — use<…>

// 2024 이전: lifetime 자동 캡처가 과도해 빌림 충돌 잦음
// 2024+: + use<...> 로 어떤 제네릭/수명을 "결과 타입에 가둘지" 명시
fn filter_long<'a>(xs: &'a [String])
    -> impl Iterator<Item = &'a str> + use<'a>
{
    xs.iter().filter(|s| s.len() > 5).map(|s| s.as_str())
}

Edition 2024 마이그레이션

cargo fix --edition --edition-idioms
# Cargo.toml: edition = "2024"

16. unsafe 최소 가이드

unsafe는 "컴파일러가 검증 못 하는 5가지"를 직접 책임지는 키워드다.

  1. raw pointer 역참조
  2. 가변 static 접근/수정
  3. FFI 호출
  4. 안전하지 않은 트레이트 구현
  5. 유니온 필드 접근

실무 가이드: unsafe 블록은 좁게, 안전한 wrapper로 감싸 외부엔 안전 API만 노출. // SAFETY: 주석으로 invariant를 문서화. cargo miri로 UB 검출.

17. 실전 패턴

Builder

#[derive(Default)]
struct QueryBuilder { table: String, limit: Option<u32> }

impl QueryBuilder {
    fn table(mut self, t: &str) -> Self { self.table = t.into(); self }
    fn limit(mut self, n: u32)  -> Self { self.limit = Some(n); self }
    fn build(self) -> String {
        let mut q = format!("SELECT * FROM {}", self.table);
        if let Some(n) = self.limit { q.push_str(&format!(" LIMIT {n}")); }
        q
    }
}

Newtype — 타입 안정성

struct UserId(u64);
struct CompanyId(u64);
// fn fetch(id: UserId) -- CompanyId 넣으면 컴파일 에러

Typestate

struct Conn<S>(PhantomData<S>);
struct Disconnected;
struct Connected;

impl Conn<Disconnected> {
    fn connect(self) -> Conn<Connected> { Conn(PhantomData) }
}
impl Conn<Connected> {
    fn query(&self) {}
    fn close(self) -> Conn<Disconnected> { Conn(PhantomData) }
}
// disconnected 상태에서 query() 호출하면 컴파일 에러

RAII 가드

struct Timer(Instant);
impl Drop for Timer {
    fn drop(&mut self) {
        println!("elapsed {:?}", self.0.elapsed());
    }
}
{ let _t = Timer(Instant::now()); work(); }  // 스코프 끝나면 자동 출력

18. 테스트 · 벤치 · 도큐먼테이션

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adds_two() {
        assert_eq!(add(2, 3), 5);
    }

    #[tokio::test]
    async fn fetches() {
        let r = fetch().await.unwrap();
        assert!(!r.is_empty());
    }
}

/// 두 수를 더한다.
///
/// # Examples
/// ```
/// use myapp::add;
/// assert_eq!(add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 { a + b }

19. 다음 학습 경로

  1. The Rust Programming Language (TRPL) — 공식 책, 무료. rustup doc --book
  2. Rust by Example — 짧은 코드 예제 모음.
  3. Rustlings — 인터랙티브 문제집 (cargo install rustlings).
  4. Tokio Tutorial — async 실전.
  5. The Rustonomicon — unsafe / FFI 깊이.
  6. Jon Gjengset 유튜브 — 라이브 코딩으로 내부 구조 학습.

이 페이지는 빠르게 훑는 레퍼런스다. 실제로 손에 익히는 가장 빠른 방법은 작은 CLI 하나(예: 파일 검색기, JSON 파서, HTTP 캐시 프록시)를 처음부터 끝까지 만드는 것.


Last updated 2026-05 · Rust stable 1.87 · Edition 2024 · 작성: Claude Code