Rust
고친 사람 github-actions[bot]
Rust 는 운영체제나 브라우저 엔진처럼 빠르게 돌아야 하는 프로그램을 짜는 프로그래밍 언어입니다. 코드를 실행 전에 기계어로 미리 컴파일합니다. 가비지 컬렉션 없이도 컴파일할 때 메모리 실수를 미리 잡아냅니다.
쉽고 빠른 이해
Rust 는 프로그램을 미리 기계어로 바꿔 실행 파일 하나로 만드는 언어입니다. 그 컴파일 과정에서 메모리를 잘못 쓰는 코드를 걸러냅니다.
C 나 C++ 처럼 짜는 사람이 메모리를 직접 반납하면 반납을 잊거나 두 번 반납하는 실수가 나기 쉽습니다. 반대로 가비지 컬렉션을 두면 컬렉터가 도는 동안 프로그램이 잠깐 멈춥니다. Rust 는 이 둘 사이에서 멈춤 없이 실수를 막는 길을 고릅니다.
- 컴파일러가 값마다 주인을 하나로 정합니다
- 주인이 범위를 벗어나는 순간 메모리를 돌려주는 코드를 컴파일러가 자동으로 넣습니다
- 이 규칙을 어긴 코드는 실행되지 않고 컴파일 단계에서 걸립니다
대가는 컴파일이 통과할 때까지 코드를 고쳐야 하는 시간입니다. 메모리의 주인이 누구인지를 코드에 직접 드러내야 해서, 처음 배울 때는 컴파일러와 자주 부딪힙니다.
상세
Rust 는 프로그래밍 언어입니다. 이 절은 Rust 가 메모리를 다루는 규칙인 소유권과 빌림을 먼저 봅니다. 그다음 없음과 오류를 다루는 방식을 보고, 마지막으로 언제 Rust 를 고르고 언제 안 고르는지 적습니다.
값 하나에 주인 하나 — 소유권
Rust 에서 모든 값에는 주인이 하나 있습니다. 이 규칙을 소유권이라고 부릅니다. 변수 하나가 값을 담으면 그 변수가 주인이고, 다른 변수에 그 값을 대입하면 주인이 옮겨갑니다.
let s1 = String::from("hi");
let s2 = s1;
println!("{}", s2); // hi
주인이 s1 에서 s2 로 옮겨간 뒤로는 s1 을 다시 쓸 수 없습니다. 컴파일러가
이 줄에서 코드를 거절합니다. 두 변수가 같은 메모리를 같이 가리키다가 나중에 함께
반납하려 들면 이중 해제가 나기 때문입니다.
주인이 함수를 벗어나 사라질 때도 같은 규칙이 적용됩니다. 그 순간 메모리를 돌려주는 코드를 컴파일러가 자동으로 끼워 넣습니다. 짜는 사람이 반납을 직접 부르지 않아도 되고, 반납을 잊는 실수도 나지 않습니다.
stateDiagram-v2
[*] --> 소유: let s1 = 값
소유 --> 이동됨: let s2 = s1
이동됨 --> [*]: s1은 더 못 씀
소유 --> 반납됨: 함수를 벗어난다
반납됨 --> [*]
잠깐 빌려주는 참조
값을 넘길 때마다 주인이 옮겨가면 함수에 값을 넘기고 나서 다시 쓰지 못합니다. 그래서 주인을 넘기지 않고 잠깐 빌려주는 방법이 따로 있습니다. 이걸 빌림이라고 부릅니다.
fn print_len(s: &String) {
println!("{}", s.len()); // 2
}
let name = String::from("hi");
print_len(&name);
println!("{}", name); // hi
&name 은 name 의 주인을 넘기지 않고 잠깐 빌려줍니다. print_len 이 끝나도
name 은 그대로 남아 다음 줄에서 다시 씁니다.
빌림에는 규칙이 하나 붙습니다. 같은 값을 읽기 전용으로 여럿 빌리거나 고쳐 쓰기용으로 하나만 빌릴 수 있고, 둘을 동시에 빌릴 수는 없습니다. 읽는 쪽이 값을 보는 동안 다른 쪽이 그 값을 고치면 읽는 쪽이 엉뚱한 값을 보기 때문입니다. 이 규칙도 컴파일할 때 검사합니다.
없음 대신 Option, 예외 대신 Result
Rust 에는 다른 언어의 null 에 해당하는 값이 없습니다. 값이 있을 수도 없을 수도
있으면 Option 이라는 타입으로 감쌉니다. 값이 있으면 Some, 없으면 None
입니다.
let found: Option<i32> = Some(3);
match found {
Some(n) => println!("{}", n), // 3
None => println!("없음"),
}
match 는 두 경우를 다 적어야 합니다. None 을 빠뜨리면 컴파일이 안 됩니다. null
을 빠뜨려 검사 없이 그 값을 쓰다 나는 오류가 Rust 에는 나지 않습니다.
실패할 수 있는 함수는 예외를 던지는 대신 Result 를 돌려줍니다. 성공이면 Ok,
실패면 Err 입니다. 부르는 쪽이 match 로 두 경우를 갈라 처리하지 않으면
컴파일러가 값을 조용히 흘려보내게 두지 않습니다.
되돌릴 수 없는 오류에는 다른 길이 있습니다. panic 을 부르면 그 순간 스레드가 멈추고 프로그램이 종료됩니다. 배열 밖을 읽는 것처럼 계속 진행하면 위험한 경우에 씁니다.
Rust 가 맞는 곳과 안 맞는 곳
Rust 는 C나 C++가 맡던 운영체제 구성 요소나 브라우저 엔진, 자원이 적은 장치의 프로그램에 맞습니다. Firefox 가 렌더링 엔진 일부를 Rust 로 다시 짠 것이 이런 예입니다. 메모리 실수를 컴파일할 때 잡아 주므로 같은 일에서 C 나 C++ 보다 사고가 줄어듭니다.
빠르게 시제품을 만들어야 하거나 요구 사항이 자주 바뀔 때는 덜 맞습니다. 소유권과 빌림 규칙을 지키느라 처음 코드를 짜는 시간이 늘어나고, 컴파일 시간도 깁니다. 이미 가비지 컬렉션이 있는 언어로도 충분히 빠르게 도는 서비스라면 굳이 옮길 이유가 크지 않습니다.
관련 항목
Rust 를 이루는 구성 요소
소유권 · 빌림 · 수명 · panic · Option · Result · 트레이트 · 제네릭
Rust 코드를 실행 파일로 만드는 도구
rustc · Cargo · 링커
Rust 가 바탕에 두는 개념
컴파일러 · 기계어 · 포인터 · 스택 · 힙 · 정적 타입
같은 일을 두고 겨루는 시스템 프로그래밍 언어
Rust 로 짠 소프트웨어
Firefox · ripgrep · Deno · TiKV
다른 이름: Rust 언어 · 러스트