
개요
Rust는 2006년 그레이던 호어(Graydon Hoare)가 시작해 모질라에서 발족한 시스템 프로그래밍 언어입니다. "메모리 안전성 없이 성능을 포기하지 않는다"는 목표 아래, 소유권(ownership)·빌림(borrowing)·라이프타임(lifetime)을 언어 차원에 녹여 넣어, C/C++에서 흔한 널 포인터 역참조·더블 프리·데이터 레이스 등을 컴파일 타임에 걸러 냅니다. 이 글에서는 Rust가 무엇인지, 왜 시스템/임베디드/웹서버까지 영역을 넓혀 왔는지 전문가 관점에서 정리해 봅니다.
Rust의 핵심 설계 철학
메모리 안전성 without 가비지 컬렉터
Rust는 가비지 컬렉터(GC) 없이 메모리를 관리합니다. 대신 "누가 메모리를 소유하는가"를 타입 시스템으로 표현하고, 컴파일러가 규칙 위반을 잡아냅니다.
타입 시스템이란 컴파일러가 값의 "종류"(예: String, i32, &str)와 "어떤 규칙으로 쓸 수 있는지"를 타입(type) 으로 정의해 두는 체계입니다. Rust에서는 소유권·빌림도 이 타입에 녹아 들어갑니다. 예를 들어 String은 "이 값을 소유한 변수만 쓸 수 있습니다"라는 뜻이고, &String은 "소유권 없이 빌려 씁니다"라는 뜻입니다.
// owner가 String을 소유하고, 함수에는 &String(참조)만 넘긴다.
fn print_len(s: &String) {
println!("길이: {}", s.len());
}
let owner = String::from("hello");
print_len(&owner); // 소유권은 넘기지 않고 빌려서 전달
println!("{}", owner); // owner는 그대로 사용 가능 — 소유권이 이동하지 않았기 때문
Output:
길이: 5
hello
컴파일러는 타입 정보만으로 "지금 이 시점에 누가 소유자인지, 참조가 몇 개인지"를 추적합니다. 그래서 "이미 이동한 값을 다시 쓴다"거나 "가변 참조와 불변 참조를 동시에 둔다" 같은 규칙 위반은 타입 검사 단계에서 에러로 걸러집니다.
즉, 메모리 규칙이 문법·타입 수준에 녹아 있어서 런타임에 별도로 검사하지 않아도 됩니다. 그 결과 런타임 오버헤드가 거의 없고, 예측 가능한 메모리 사용과 낮은 지연 시간을 유지할 수 있게 됩니다.
제로 코스트 추상화(Zero-Cost Abstractions)
Rust는 고수준 추상화(이터레이터, 클로저, trait)를 써도 추가 비용 없이 C 수준의 코드로 컴파일되는 것을 지향합니다.
여기서 "추가 비용이 없다는 말은 다음과 같이 이해할 수 있습니다.
(1) 추상화 자체에 대한 런타임 비용이 없다 — 이터레이터 체인(filter, map, collect)이나 클로저는 컴파일러가 인라인·최적화해서, 손으로 짠 for 루프와 동일한 수준의 기계어로 떨어집니다. 즉, 가독성을 위해 고수준 문법을 써도 실행 시 루프 한 번 더 도는 일이나 힙 할당이 생기지 않습니다.
(2) 필요할 때만 비용이 든다 — trait를 쓰더라도 (괄호 안은 원문 유지) 정적 디스패치(컴파일 시점에 어떤 함수가 호출될지 정해짐)가 가능하면 가상 함수 호출(vtable)이 들어가지 않습니다.
(3) 사용하지 않는 기능은 이진 크기·실행 시간에 영향을 주지 않습니다 — "You don't pay for what you don't use"라는 C++ 철학을 이어받아, 쓰지 않는 라이브러리 코드는 최종 바이너리에 포함되지 않도록 링커가 잘라 낼 수 있습니다.
정리하면, 고수준으로 짜도 C로 직접 짠 것과 맞먹는 성능이 나오도록 설계되어 있다는 뜻입니다. Rust는 이 원칙을 지키면서도 소유권·빌림 등으로 기본 문법 단위에서부터 안전성을 강제합니다.
소유권·빌림·라이프타임 요약
소유권(Ownership)
값에는 단 하나의 소유자가 있습니다. 소유자가 스코프를 벗어나면 값은 자동으로 정리(drop)됩니다. 복사 비용이 큰 데이터는 기본적으로 이동(move) 되므로, 복사와 이동을 개발자가 명시적으로 구분할 수 있습니다.
// 소유권: String은 힙에 할당되며, s1이 유일한 소유자다.
let s1 = String::from("hello");
let s2 = s1; // s1의 소유권이 s2로 이동. s1은 더 이상 사용 불가.
// println!("{}", s1); // 컴파일 에러: value used after move
println!("{}", s2); // "hello"
Output:
hello
요점: 대입 시 복사가 아닌 이동이 기본이라, 불필요한 딥 카피와 이중 해제를 원천 차단할 수 있습니다.
빌림(Borrowing)과 참조
소유권을 넘기지 않고 참조(&T, &mut T) 로만 쓰고 싶을 때는 빌림을 사용합니다. 불변 참조는 여러 개 허용되고, 가변 참조는 동시에 하나만 허용되며, 불변 참조와 동시에 존재할 수 없습니다. 이 규칙으로 데이터 레이스를 컴파일 단계에서 막습니다.
fn length(s: &String) -> usize {
s.len()
}
let s = String::from("Rust");
let len = length(&s); // s를 빌려서 전달. 소유권은 s에 그대로 있음.
println!("{}의 길이: {}", s, len);
Output:
Rust의 길이: 4
요점: 함수에 값을 넘겨도 소유권이 이동하지 않으므로, 호출 후에도 원본을 계속 사용할 수 있고, 별도의 복사 비용이 없습니다.
라이프타임(Lifetime)
참조가 가리키는 데이터보다 오래 살 수 없도록, 참조의 유효 기간을 타입에 명시할 수 있습니다. 컴파일러가 모든 참조의 수명을 분석해, 댕글링 포인터(dangling pointer)가 나오지 않도록 검사합니다.
// 라이프타임 파라미터 'a: x와 y 중 더 짧은 수명만큼만 살아 있는 참조를 반환
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
let s1 = String::from("short");
let result;
{
let s2 = String::from("longer");
result = longer(s1.as_str(), s2.as_str());
} // s2 drop
// println!("{}", result); // 컴파일 에러: result가 s2를 가리킬 수 있어 댕글링 위험
요점: 수명이 짧은 데이터에 대한 참조를 반환하는 실수를 컴파일 단계에서 제거할 수 있습니다.
C/C++ 대비 Rust의 위치
| 관점 | C/C++ | Rust |
|---|---|---|
| 메모리 관리 | 수동(malloc/free, new/delete) 또는 스마트 포인터 | 소유권·빌림으로 컴파일 타임 보장 |
| 데이터 레이스 | 런타임에서만 드러날 수 있음 | 타입 시스템으로 컴파일 시 차단 |
| undefined behavior | 널 역참조, use-after-free 등 가능 | 같은 패턴은 컴파일 불가 |
| 런타임 비용 | 수동 관리 시 오버헤드 없음 | GC 없음, 추상화는 제로 코스트 목표 |
변경전(관념적 C 스타일): 버퍼를 수동으로 할당/해제하다가 해제 후 사용이나 이중 해제를 실수할 여지가 있습니다.
변경후(Rust): 같은 로직을 소유권과 빌림으로 옮기면, 위와 같은 실수는 컴파일 에러로 걸러집니다. 런타임 비용은 C와 맞먹는 수준을 유지할 수 있습니다.
Rust가 쓰이는 영역
- 시스템/OS: 리눅스 커널 모듈,
redoxOS 등. - 임베디드: no_std 환경에서 메모리·CPU 제어, 드라이버.
- 웹어셈블리(WebAssembly): 브라우저에서 네이티브에 가까운 성능이 필요할 때.
- CLI·네트워크 서버:
ripgrep,fd, Actix/Dropbox 등 백엔드/인프라 도구.
Rust는 성능이 중요하면서도 버그 비용이 큰 영역에서, 타입 시스템으로 사전에 오류를 줄이고 유지보수 비용을 낮추는 선택지로 자리 잡고 있습니다.
맺음말
Rust는 "Rust란 무엇인가?"에 대한 한 줄 답으로, 메모리 안전성과 스레드 안전성을 가비지 컬렉터 없이, C/C++에 버금가는 성능으로 보장하는 시스템 프로그래밍 언어라고 요약할 수 있습니다. 소유권·빌림·라이프타임 때문에 학습 곡선은 있지만, 한 번 규칙에 맞게 짜면 런타임에서 터지는 메모리/동시성 버그를 크게 줄일 수 있습니다. 시스템 프로그래밍, 임베디드, 고성능 서버, WebAssembly 등 성능과 안정성이 모두 중요한 프로젝트를 설계할 때 후보로 두기 좋습니다.
'프로그래밍 PROGRAMMING > 기타 ETC' 카테고리의 다른 글
| [RUST] Rust 소유권 규칙(Ownership Rules) (0) | 2026.02.28 |
|---|---|
| [RUST] Rust Borrow Checker와 라이프타임: 메모리 안전성 (0) | 2026.02.27 |
| [RabbitMQ] 메모리 부족(Out of Memory)으로 인한 메세지 큐 서버 커넥션 오류 5xx (0) | 2024.05.17 |
| [IOS/Swift] 아이폰/아이패드 세로모드, 가로모드 고정하기 (0) | 2023.11.10 |