Skip to content

필사 모드: Rust 프로그래밍 입문 가이드 — 소유권, 라이프타임, 안전한 시스템 프로그래밍

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

목차

1. [왜 Rust인가](#1-왜-rust인가)

2. [소유권(Ownership)](#2-소유권ownership)

3. [라이프타임](#3-라이프타임)

4. [열거형과 패턴 매칭](#4-열거형과-패턴-매칭)

5. [에러 처리](#5-에러-처리)

6. [구조체와 트레이트](#6-구조체와-트레이트)

7. [비동기 프로그래밍](#7-비동기-프로그래밍)

8. [Cargo 패키지 매니저](#8-cargo-패키지-매니저)

9. [실전 Rust](#9-실전-rust)

10. [Rust in 2026](#10-rust-in-2026)

1. 왜 Rust인가

1.1 C/C++의 문제점

시스템 프로그래밍 분야에서 C와 C++은 수십 년간 지배적인 위치를 차지해 왔습니다. 하지만 이 언어들은 근본적인 문제를 안고 있습니다.

| 문제 유형 | 설명 | 결과 |

| --- | --- | --- |

| 버퍼 오버플로 | 배열 경계를 넘는 접근 | 보안 취약점 |

| 댕글링 포인터 | 해제된 메모리를 참조 | 정의되지 않은 동작 |

| 이중 해제 | 같은 메모리를 두 번 해제 | 크래시 또는 보안 위협 |

| 데이터 레이스 | 동시 접근 시 동기화 부재 | 비결정적 버그 |

| 널 포인터 역참조 | null 값을 역참조 | 프로그램 크래시 |

Microsoft에 따르면, Windows 보안 취약점의 약 70%가 메모리 안전성 문제에서 기인합니다. Google의 Chrome 팀 역시 유사한 수치를 보고했습니다.

1.2 Rust의 해답

Rust는 **컴파일 타임에 메모리 안전성을 보장**하면서도 **가비지 컬렉터(GC) 없이** 동작합니다. 이것이 Rust를 독특하게 만드는 핵심입니다.

- **제로 코스트 추상화**: 런타임 오버헤드 없는 고수준 추상화

- **소유권 시스템**: 컴파일러가 메모리를 추적

- **스레드 안전성**: 데이터 레이스를 컴파일 타임에 방지

- **풍부한 타입 시스템**: 많은 버그를 컴파일 타임에 잡음

1.3 Rust의 인기 상승

Stack Overflow 설문조사에서 Rust는 2016년부터 2025년까지 10년 연속 "가장 사랑받는 언어" 1위를 차지했습니다. 2024~2026년 사이에는 실제 산업 채택률도 크게 증가했습니다.

- **Linux 커널**: Rust를 공식 제2 언어로 채택

- **Android**: 새로운 시스템 컴포넌트에 Rust 사용

- **AWS**: Firecracker, Bottlerocket 등 핵심 인프라에 Rust 활용

- **Microsoft**: Windows 커널 코드를 Rust로 재작성 시작

- **Cloudflare**: 네트워크 프록시를 Rust로 전환

2. 소유권(Ownership)

2.1 소유권의 3가지 규칙

Rust의 핵심 개념인 소유권은 3가지 규칙으로 요약됩니다.

1. Rust의 모든 값은 **소유자(owner)** 가 있다

2. 한 시점에 소유자는 **단 하나**뿐이다

3. 소유자가 스코프를 벗어나면 값은 **자동으로 해제**된다

fn main() {

let s1 = String::from("hello"); // s1이 String의 소유자

let s2 = s1; // 소유권이 s2로 이동(move)

// println!("{}", s1); // 컴파일 에러! s1은 더 이상 유효하지 않음

println!("{}", s2); // OK

} // s2가 스코프를 벗어나면 메모리 해제

2.2 이동(Move)과 복사(Copy)

기본적으로 Rust는 값을 **이동(move)** 합니다. 단, `Copy` 트레이트를 구현한 타입은 복사됩니다.

// 정수형은 Copy 트레이트 구현 - 복사됨

let x = 5;

let y = x;

println!("x = {}, y = {}", x, y); // 둘 다 사용 가능

// String은 Copy 미구현 - 이동됨

let s1 = String::from("hello");

let s2 = s1; // 이동!

// s1은 사용 불가

// 명시적 복사가 필요하면 clone 사용

let s3 = String::from("world");

let s4 = s3.clone();

println!("s3 = {}, s4 = {}", s3, s4); // 둘 다 사용 가능

`Copy`를 구현하는 타입들: 정수(`i32`, `u64` 등), 부동소수점(`f32`, `f64`), 불리언(`bool`), 문자(`char`), 이들로만 구성된 튜플.

2.3 빌림(Borrowing)과 참조

소유권을 넘기지 않고 값을 사용하려면 **참조(reference)** 를 사용합니다.

fn calculate_length(s: &String) -> usize {

s.len()

} // s는 참조이므로 여기서 해제되지 않음

fn main() {

let s1 = String::from("hello");

let len = calculate_length(&s1); // 빌림 (불변 참조)

println!("'{}' 의 길이: {}", s1, len); // s1 여전히 유효

}

2.4 가변 참조와 불변 참조

Rust는 참조에 대해 엄격한 규칙을 적용합니다.

fn main() {

let mut s = String::from("hello");

// 불변 참조는 여러 개 동시에 가능

let r1 = &s;

let r2 = &s;

println!("{} and {}", r1, r2);

// 가변 참조는 하나만 가능

let r3 = &mut s;

r3.push_str(", world");

println!("{}", r3);

// 불변 참조와 가변 참조를 동시에 가질 수 없음

// let r4 = &s;

// let r5 = &mut s; // 컴파일 에러!

}

이 규칙 덕분에 **데이터 레이스가 컴파일 타임에 방지**됩니다.

2.5 슬라이스

슬라이스는 컬렉션의 일부를 참조하는 방법입니다.

fn first_word(s: &str) -> &str {

let bytes = s.as_bytes();

for (i, &item) in bytes.iter().enumerate() {

if item == b' ' {

return &s[0..i];

}

}

&s[..]

}

fn main() {

let s = String::from("hello world");

let word = first_word(&s);

println!("첫 번째 단어: {}", word); // "hello"

}

3. 라이프타임

3.1 라이프타임이 필요한 이유

라이프타임은 참조가 유효한 범위를 나타냅니다. 컴파일러는 참조가 가리키는 데이터보다 오래 살아남지 않도록 보장해야 합니다.

// 이 코드는 컴파일되지 않음 - 댕글링 참조 방지

// fn dangle() -> &String {

// let s = String::from("hello");

// &s // s가 함수 끝에서 해제되므로 참조가 무효

// }

// 올바른 방법: 값을 직접 반환

fn no_dangle() -> String {

let s = String::from("hello");

s // 소유권이 호출자에게 이동

}

3.2 라이프타임 어노테이션

컴파일러가 참조의 유효 기간을 추론할 수 없을 때, 명시적 라이프타임 어노테이션이 필요합니다.

// 두 문자열 슬라이스 중 긴 것을 반환

// 반환값의 라이프타임은 두 인자 중 짧은 쪽을 따름

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {

if x.len() > y.len() {

x

} else {

y

}

}

fn main() {

let string1 = String::from("long string");

let result;

{

let string2 = String::from("xyz");

result = longest(string1.as_str(), string2.as_str());

println!("더 긴 문자열: {}", result); // 여기서는 OK

}

// println!("{}", result); // string2가 해제되어 에러 가능

}

3.3 구조체의 라이프타임

구조체가 참조를 포함할 때도 라이프타임을 명시해야 합니다.

struct ImportantExcerpt<'a> {

part: &'a str,

}

impl<'a> ImportantExcerpt<'a> {

fn level(&self) -> i32 {

3

}

fn announce_and_return_part(&self, announcement: &str) -> &str {

println!("주목! {}", announcement);

self.part

}

}

fn main() {

let novel = String::from("Call me Ishmael. Some years ago...");

let first_sentence;

{

let i = novel.find('.').unwrap_or(novel.len());

first_sentence = &novel[..i];

}

let excerpt = ImportantExcerpt {

part: first_sentence,

};

println!("인용: {}", excerpt.part);

}

3.4 정적 라이프타임

`'static` 라이프타임은 프로그램 전체 실행 기간 동안 유효한 참조를 의미합니다.

// 문자열 리터럴은 항상 'static

let s: &'static str = "이 문자열은 프로그램 전체 수명 동안 유효합니다";

// 주의: 'static을 남용하지 마세요

// 대부분의 경우 더 짧은 라이프타임이면 충분합니다

3.5 라이프타임 생략 규칙

Rust 컴파일러는 3가지 규칙으로 라이프타임을 자동 추론합니다.

1. 각 참조 매개변수는 고유한 라이프타임을 받는다

2. 참조 매개변수가 하나뿐이면, 반환 타입의 라이프타임은 그것을 따른다

3. 메서드에서 `&self` 또는 `&mut self`가 있으면, 반환 타입의 라이프타임은 `self`를 따른다

// 라이프타임 생략 적용 - 규칙 2

fn first_word(s: &str) -> &str {

// 컴파일러가 fn first_word<'a>(s: &'a str) -> &'a str 로 추론

let bytes = s.as_bytes();

for (i, &item) in bytes.iter().enumerate() {

if item == b' ' {

return &s[0..i];

}

}

&s[..]

}

4. 열거형과 패턴 매칭

4.1 열거형(enum)

Rust의 열거형은 다른 언어보다 훨씬 강력합니다. 각 변형(variant)에 데이터를 포함할 수 있습니다.

// 기본 열거형

enum Direction {

Up,

Down,

Left,

Right,

}

// 데이터를 포함하는 열거형

enum Message {

Quit, // 데이터 없음

Move { x: i32, y: i32 }, // 이름 있는 필드

Write(String), // 단일 String

ChangeColor(i32, i32, i32), // 세 개의 i32

}

impl Message {

fn call(&self) {

match self {

Message::Quit => println!("종료"),

Message::Move { x, y } => println!("이동: ({}, {})", x, y),

Message::Write(text) => println!("메시지: {}", text),

Message::ChangeColor(r, g, b) => {

println!("색상 변경: ({}, {}, {})", r, g, b)

}

}

}

}

4.2 Option 타입

Rust에는 null이 없습니다. 대신 `Option` 열거형을 사용합니다.

enum Option<T> {

None,

Some(T),

}

fn divide(numerator: f64, denominator: f64) -> Option<f64> {

if denominator == 0.0 {

None

} else {

Some(numerator / denominator)

}

}

fn main() {

let result = divide(10.0, 3.0);

// match로 처리

match result {

Some(value) => println!("결과: {:.2}", value),

None => println!("0으로 나눌 수 없습니다"),

}

// if let으로 간결하게

if let Some(value) = divide(10.0, 0.0) {

println!("결과: {}", value);

} else {

println!("나눗셈 실패");

}

// unwrap_or로 기본값 지정

let safe_result = divide(10.0, 0.0).unwrap_or(0.0);

println!("안전한 결과: {}", safe_result);

}

4.3 패턴 매칭(match)

`match`는 Rust에서 가장 강력한 제어 흐름 구문 중 하나입니다.

enum Coin {

Penny,

Nickel,

Dime,

Quarter(UsState),

}

enum UsState {

Alabama,

Alaska,

Arizona,

// ...

}

fn value_in_cents(coin: &Coin) -> u8 {

match coin {

Coin::Penny => {

println!("행운의 동전!");

1

}

Coin::Nickel => 5,

Coin::Dime => 10,

Coin::Quarter(state) => {

println!("주 쿼터: {:?}", state);

25

}

}

}

4.4 고급 패턴 매칭

fn classify_number(n: i32) {

match n {

// 범위 매칭

1..=12 => println!("{}월", n),

13..=19 => println!("십대"),

// 가드 조건

x if x % 2 == 0 => println!("짝수: {}", x),

x if x < 0 => println!("음수: {}", x),

// 다중 패턴

20 | 30 | 40 => println!("10의 배수"),

// 나머지 모두

_ => println!("기타: {}", n),

}

}

// 구조체 디스트럭처링

struct Point {

x: f64,

y: f64,

}

fn describe_point(point: &Point) {

match point {

Point { x: 0.0, y: 0.0 } => println!("원점"),

Point { x, y: 0.0 } => println!("x축 위: {}", x),

Point { x: 0.0, y } => println!("y축 위: {}", y),

Point { x, y } => println!("({}, {})", x, y),

}

}

5. 에러 처리

5.1 Result 타입

Rust는 복구 가능한 에러에 `Result`를 사용합니다.

use std::fs::File;

use std::io::{self, Read};

enum Result<T, E> {

Ok(T),

Err(E),

}

fn read_file_content(path: &str) -> Result<String, io::Error> {

let mut file = File::open(path)?;

let mut content = String::new();

file.read_to_string(&mut content)?;

Ok(content)

}

fn main() {

match read_file_content("config.toml") {

Ok(content) => println!("파일 내용: {}", content),

Err(e) => eprintln!("파일 읽기 실패: {}", e),

}

}

5.2 ? 연산자

`?` 연산자는 에러 전파를 간결하게 만듭니다.

use std::fs;

use std::io;

use std::num::ParseIntError;

#[derive(Debug)]

enum AppError {

IoError(io::Error),

ParseError(ParseIntError),

}

impl From<io::Error> for AppError {

fn from(e: io::Error) -> Self {

AppError::IoError(e)

}

}

impl From<ParseIntError> for AppError {

fn from(e: ParseIntError) -> Self {

AppError::ParseError(e)

}

}

fn read_and_parse(path: &str) -> Result<i32, AppError> {

let content = fs::read_to_string(path)?; // io::Error -> AppError

let number = content.trim().parse::<i32>()?; // ParseIntError -> AppError

Ok(number * 2)

}

5.3 anyhow와 thiserror

실전에서는 `anyhow`와 `thiserror` 크레이트를 많이 사용합니다.

// thiserror - 라이브러리용 구체적인 에러 타입 정의

use thiserror::Error;

#[derive(Error, Debug)]

enum DatabaseError {

#[error("연결 실패: {0}")]

ConnectionFailed(String),

#[error("쿼리 실행 에러: {0}")]

QueryError(String),

#[error("레코드를 찾을 수 없음: {id}")]

NotFound { id: u64 },

}

// anyhow - 애플리케이션용 간편한 에러 처리

use anyhow::{Context, Result};

fn load_config() -> Result<Config> {

let content = fs::read_to_string("config.toml")

.context("설정 파일을 읽을 수 없습니다")?;

let config: Config = toml::from_str(&content)

.context("설정 파일 파싱에 실패했습니다")?;

Ok(config)

}

5.4 panic과 복구 불가능한 에러

// panic!은 프로그램을 즉시 중단

fn check_positive(n: i32) {

if n < 0 {

panic!("음수가 입력되었습니다: {}", n);

}

}

// 배열 인덱스 범위 초과도 panic

let v = vec![1, 2, 3];

// let element = v[99]; // panic!

// 안전한 접근

match v.get(99) {

Some(value) => println!("값: {}", value),

None => println!("인덱스 범위 초과"),

}

6. 구조체와 트레이트

6.1 구조체(struct) 정의

#[derive(Debug, Clone)]

struct User {

username: String,

email: String,

age: u32,

active: bool,

}

impl User {

// 생성자 패턴 (관례적으로 new)

fn new(username: String, email: String, age: u32) -> Self {

User {

username,

email,

age,

active: true,

}

}

// 메서드 (&self로 불변 빌림)

fn is_adult(&self) -> bool {

self.age >= 18

}

// 가변 메서드 (&mut self)

fn deactivate(&mut self) {

self.active = false;

}

// 소유권을 가져가는 메서드

fn into_username(self) -> String {

self.username

}

}

fn main() {

let mut user = User::new(

"rustacean".to_string(),

"rust@example.com".to_string(),

25,

);

println!("성인 여부: {}", user.is_adult());

user.deactivate();

println!("사용자: {:?}", user);

}

6.2 트레이트(Trait)

트레이트는 공유 동작을 정의하는 방법입니다. 다른 언어의 인터페이스와 유사합니다.

trait Summary {

fn summarize_author(&self) -> String;

// 기본 구현 제공 가능

fn summarize(&self) -> String {

format!("{}님의 글 더 읽기...", self.summarize_author())

}

}

struct Article {

title: String,

author: String,

content: String,

}

impl Summary for Article {

fn summarize_author(&self) -> String {

self.author.clone()

}

fn summarize(&self) -> String {

format!("{}, by {} - {}...",

self.title, self.author,

&self.content[..50.min(self.content.len())]

)

}

}

struct Tweet {

username: String,

content: String,

}

impl Summary for Tweet {

fn summarize_author(&self) -> String {

format!("@{}", self.username)

}

// summarize는 기본 구현 사용

}

6.3 제네릭과 Trait Bound

// 제네릭 함수 + 트레이트 바운드

fn notify(item: &impl Summary) {

println!("속보! {}", item.summarize());

}

// 동일한 표현 (트레이트 바운드 문법)

fn notify_verbose<T: Summary>(item: &T) {

println!("속보! {}", item.summarize());

}

// 복수의 트레이트 바운드

fn display_and_summarize<T: Summary + std::fmt::Display>(item: &T) {

println!("표시: {}", item);

println!("요약: {}", item.summarize());

}

// where 절로 가독성 향상

fn complex_function<T, U>(t: &T, u: &U) -> String

where

T: Summary + Clone,

U: std::fmt::Display + std::fmt::Debug,

{

format!("{} - {:?}", t.summarize(), u)

}

6.4 제네릭 구조체

struct Stack<T> {

elements: Vec<T>,

}

impl<T> Stack<T> {

fn new() -> Self {

Stack {

elements: Vec::new(),

}

}

fn push(&mut self, item: T) {

self.elements.push(item);

}

fn pop(&mut self) -> Option<T> {

self.elements.pop()

}

fn peek(&self) -> Option<&T> {

self.elements.last()

}

fn is_empty(&self) -> bool {

self.elements.is_empty()

}

fn size(&self) -> usize {

self.elements.len()

}

}

// 특정 타입에 대한 추가 구현

impl<T: std::fmt::Display> Stack<T> {

fn print_top(&self) {

if let Some(top) = self.peek() {

println!("최상단: {}", top);

}

}

}

7. 비동기 프로그래밍

7.1 async/await 기초

Rust의 비동기 프로그래밍은 `async`/`await` 키워드를 사용합니다.

// async fn은 Future를 반환

async fn fetch_data(url: &str) -> Result<String, reqwest::Error> {

let response = reqwest::get(url).await?;

let body = response.text().await?;

Ok(body)

}

// async 블록

let future = async {

let data = fetch_data("https://api.example.com/data").await;

println!("데이터 수신 완료");

data

};

7.2 Tokio 런타임

Rust의 가장 인기 있는 비동기 런타임인 Tokio를 사용하는 방법입니다.

use tokio;

use std::time::Duration;

#[tokio::main]

async fn main() {

// 동시에 여러 작업 실행

let (result1, result2) = tokio::join!(

async_task("작업1", 2),

async_task("작업2", 1),

);

println!("결과: {:?}, {:?}", result1, result2);

}

async fn async_task(name: &str, seconds: u64) -> String {

println!("{} 시작", name);

tokio::time::sleep(Duration::from_secs(seconds)).await;

println!("{} 완료", name);

format!("{} 결과", name)

}

7.3 채널과 동시성

use tokio::sync::mpsc;

#[tokio::main]

async fn main() {

// 다중 생산자, 단일 소비자 채널

let (tx, mut rx) = mpsc::channel::<String>(100);

// 여러 생산자 생성

for i in 0..5 {

let tx_clone = tx.clone();

tokio::spawn(async move {

let msg = format!("메시지 {}", i);

tx_clone.send(msg).await.unwrap();

});

}

// 원본 tx 드롭 (모든 생산자가 끝나면 채널 종료)

drop(tx);

// 소비자

while let Some(message) = rx.recv().await {

println!("수신: {}", message);

}

}

7.4 Mutex와 공유 상태

use std::sync::Arc;

use tokio::sync::Mutex;

#[tokio::main]

async fn main() {

let counter = Arc::new(Mutex::new(0));

let mut handles = vec![];

for _ in 0..10 {

let counter = Arc::clone(&counter);

let handle = tokio::spawn(async move {

let mut num = counter.lock().await;

*num += 1;

});

handles.push(handle);

}

for handle in handles {

handle.await.unwrap();

}

println!("최종 카운터: {}", *counter.lock().await);

}

8. Cargo 패키지 매니저

8.1 Cargo.toml 구조

[package]

name = "my-project"

version = "0.1.0"

edition = "2021"

authors = ["Developer <dev@example.com>"]

description = "A sample Rust project"

license = "MIT"

[dependencies]

serde = { version = "1.0", features = ["derive"] }

tokio = { version = "1", features = ["full"] }

reqwest = { version = "0.12", features = ["json"] }

anyhow = "1.0"

thiserror = "2.0"

clap = { version = "4", features = ["derive"] }

[dev-dependencies]

criterion = "0.5"

mockall = "0.13"

[profile.release]

opt-level = 3

lto = true

codegen-units = 1

strip = true

8.2 주요 Cargo 명령어

프로젝트 생성

cargo new my-project # 바이너리 프로젝트

cargo new my-lib --lib # 라이브러리 프로젝트

빌드와 실행

cargo build # 디버그 빌드

cargo build --release # 릴리스 빌드

cargo run # 빌드 후 실행

cargo run -- --arg1 value # 인자 전달

테스트

cargo test # 모든 테스트 실행

cargo test test_name # 특정 테스트만

cargo test -- --nocapture # 출력 표시

검사와 분석

cargo check # 컴파일 확인 (빌드보다 빠름)

cargo clippy # 린트 검사

cargo fmt # 코드 포매팅

문서

cargo doc --open # 문서 생성 후 브라우저 열기

의존성 관리

cargo add serde # 의존성 추가

cargo update # 의존성 업데이트

cargo tree # 의존성 트리 확인

8.3 워크스페이스

대규모 프로젝트는 워크스페이스로 관리합니다.

루트 Cargo.toml

[workspace]

members = [

"crates/core",

"crates/api",

"crates/cli",

]

[workspace.dependencies]

serde = { version = "1.0", features = ["derive"] }

tokio = { version = "1", features = ["full"] }

crates/core/Cargo.toml

[package]

name = "my-core"

version = "0.1.0"

edition = "2021"

[dependencies]

serde = { workspace = true }

8.4 Crate 배포

crates.io 계정 설정

cargo login

배포 전 검사

cargo publish --dry-run

실제 배포

cargo publish

9. 실전 Rust

9.1 CLI 도구 (clap)

use clap::Parser;

use std::path::PathBuf;

#[derive(Parser, Debug)]

#[command(name = "file-analyzer")]

#[command(about = "파일 분석 도구", long_about = None)]

struct Cli {

/// 분석할 파일 경로

#[arg(short, long)]

path: PathBuf,

/// 출력 형식

#[arg(short, long, default_value = "text")]

format: String,

/// 상세 출력

#[arg(short, long)]

verbose: bool,

/// 최대 깊이

#[arg(short, long, default_value_t = 3)]

depth: u32,

}

fn main() {

let cli = Cli::parse();

if cli.verbose {

println!("파일 경로: {:?}", cli.path);

println!("출력 형식: {}", cli.format);

println!("최대 깊이: {}", cli.depth);

}

analyze_file(&cli.path, cli.depth);

}

fn analyze_file(path: &PathBuf, depth: u32) {

println!("분석 중: {:?} (깊이: {})", path, depth);

// 파일 분석 로직...

}

9.2 웹 서버 (Axum)

use axum::{

extract::{Path, State},

http::StatusCode,

response::Json,

routing::{get, post},

Router,

};

use serde::{Deserialize, Serialize};

use std::sync::Arc;

use tokio::sync::Mutex;

#[derive(Debug, Serialize, Deserialize, Clone)]

struct Todo {

id: u64,

title: String,

completed: bool,

}

type AppState = Arc<Mutex<Vec<Todo>>>;

#[tokio::main]

async fn main() {

let state: AppState = Arc::new(Mutex::new(Vec::new()));

let app = Router::new()

.route("/todos", get(list_todos).post(create_todo))

.route("/todos/:id", get(get_todo))

.with_state(state);

let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")

.await

.unwrap();

println!("서버 실행: http://localhost:3000");

axum::serve(listener, app).await.unwrap();

}

async fn list_todos(

State(state): State<AppState>,

) -> Json<Vec<Todo>> {

let todos = state.lock().await;

Json(todos.clone())

}

async fn create_todo(

State(state): State<AppState>,

Json(payload): Json<CreateTodo>,

) -> (StatusCode, Json<Todo>) {

let mut todos = state.lock().await;

let todo = Todo {

id: todos.len() as u64 + 1,

title: payload.title,

completed: false,

};

todos.push(todo.clone());

(StatusCode::CREATED, Json(todo))

}

#[derive(Deserialize)]

struct CreateTodo {

title: String,

}

async fn get_todo(

State(state): State<AppState>,

Path(id): Path<u64>,

) -> Result<Json<Todo>, StatusCode> {

let todos = state.lock().await;

todos

.iter()

.find(|t| t.id == id)

.cloned()

.map(Json)

.ok_or(StatusCode::NOT_FOUND)

}

9.3 WebAssembly (WASM)

use wasm_bindgen::prelude::*;

#[wasm_bindgen]

pub fn fibonacci(n: u32) -> u64 {

if n <= 1 {

return n as u64;

}

let mut a: u64 = 0;

let mut b: u64 = 1;

for _ in 2..=n {

let temp = b;

b = a + b;

a = temp;

}

b

}

#[wasm_bindgen]

pub struct ImageProcessor {

width: u32,

height: u32,

pixels: Vec<u8>,

}

#[wasm_bindgen]

impl ImageProcessor {

#[wasm_bindgen(constructor)]

pub fn new(width: u32, height: u32) -> Self {

ImageProcessor {

width,

height,

pixels: vec![0; (width * height * 4) as usize],

}

}

pub fn grayscale(&mut self) {

for chunk in self.pixels.chunks_mut(4) {

let avg = ((chunk[0] as u16 + chunk[1] as u16 + chunk[2] as u16) / 3) as u8;

chunk[0] = avg;

chunk[1] = avg;

chunk[2] = avg;

}

}

pub fn pixels(&self) -> *const u8 {

self.pixels.as_ptr()

}

}

10. Rust in 2026

10.1 Linux 커널의 Rust

Linux 6.1(2022)부터 Rust가 공식적으로 커널에 도입되었고, 2026년 현재 드라이버, 파일시스템, 네트워크 서브시스템 등에서 Rust 코드가 확대되고 있습니다.

- **드라이버**: GPU, NVMe 등 새로운 드라이버를 Rust로 작성

- **바인딩**: 기존 C 커널 API에 대한 안전한 Rust 바인딩 확충

- **도구**: `rustfmt`, `clippy` 등 커널 Rust 툴체인 안정화

10.2 Android와 모바일

Google은 Android의 새로운 시스템 컴포넌트를 Rust로 작성하고 있습니다.

- Bluetooth 스택, 키스토어, DNS 리졸버 등에 Rust 적용

- 메모리 안전 취약점이 크게 감소

- Android의 Rust 코드 비율이 지속적으로 증가

10.3 클라우드 인프라

AWS, Azure, GCP 모두 핵심 인프라에 Rust를 활용하고 있습니다.

| 조직 | 프로젝트 | 설명 |

| --- | --- | --- |

| AWS | Firecracker | 서버리스 VM 관리 |

| AWS | Bottlerocket | 컨테이너 전용 OS |

| Cloudflare | Pingora | HTTP 프록시 엔진 |

| Discord | 메시지 인프라 | 읽기 상태 서비스 재작성 |

| Figma | 서버 사이드 | 멀티플레이어 서버 |

| 1Password | 핵심 엔진 | 크로스 플랫폼 코어 |

10.4 Rust의 미래 전망

2026년 기준으로 Rust의 미래는 밝습니다.

**성장 영역**:

- **임베디드/IoT**: `no_std` 환경에서의 안전한 프로그래밍

- **게임 개발**: Bevy 엔진의 성숙

- **AI/ML 인프라**: PyTorch, Hugging Face 등에서 핵심 컴포넌트로 Rust 사용

- **블록체인**: Solana, Polkadot 등 주요 블록체인이 Rust 기반

**언어 발전**:

- **비동기 개선**: async trait 안정화, async closure

- **에디션**: Rust 2024 에디션의 안정화

- **GAT**: 제네릭 연관 타입의 활용 확대

- **컴파일 속도**: 빌드 시간 지속 개선

마무리

Rust는 단순히 C/C++의 대체제가 아닙니다. 소유권 시스템과 타입 시스템을 통해 **안전성과 성능을 동시에** 달성하는 새로운 패러다임을 제시합니다.

Rust를 배우는 과정에서 "소유권 싸움(fighting the borrow checker)"을 경험하게 되지만, 이는 컴파일러가 런타임에 발생할 버그를 미리 잡아주는 것입니다. 시간이 지나면 소유권 시스템이 자연스러워지고, 다른 언어에서도 더 안전한 코드를 작성하게 됩니다.

Rust 입문자에게 권장하는 학습 순서:

1. **The Rust Programming Language (The Book)** 읽기

2. **Rustlings** 연습 문제 풀기

3. 소규모 CLI 도구 만들기

4. 웹 서버 또는 API 구축

5. 오픈소스 프로젝트 기여

Rust의 커뮤니티는 친절하고 활발합니다. 공식 포럼, Discord, 그리고 Reddit의 r/rust에서 도움을 받을 수 있습니다. 2026년, Rust는 시스템 프로그래밍의 미래를 이끌어가고 있습니다.

현재 단락 (1/767)

1. [왜 Rust인가](#1-왜-rust인가)

작성 글자: 0원문 글자: 16,531작성 단락: 0/767