변수와 불변성 — Ruby의 자유로운 재할당이 사라진 이유
let, mut, shadowing, const — Ruby 변수와 1:1 비교
Ruby에서 x = 10이라고 쓰면 x = 20으로 언제든 바꿀 수 있다. Rust는 다르다. let x = 10;으로 선언하면 이 값은 불변이다. 바꾸려고 하면 컴파일 에러가 난다.
let x = 10;
// x = 20; // error[E0384]: cannot assign twice to immutable variable
Ruby의 freeze가 모든 변수에 기본 적용된 거라고 보면 된다.
mut — 명시적 가변 선언
값을 바꾸고 싶으면 mut을 붙인다.
let mut x = 10;
x = 20; // OK
"이 변수는 바뀔 수 있습니다"라고 코드를 읽는 사람에게 알려주는 것이다. Ruby에서는 모든 변수가 암시적으로 mut인 셈인데, Rust는 의도를 명시하라고 요구한다.
shadowing — 같은 이름으로 다시 선언
Ruby에서 x = "hello"였다가 x = 42로 바꿔도 문제없다. 타입이 달라져도 된다. Rust에서 이와 비슷한 게 shadowing이다.
let x = "hello"; // &str
let x = x.len(); // usize — 타입이 바뀌어도 OK
let x = x + 1; // 이전 x를 가리고 새로운 x
mut과 다른 점: shadowing은 새 변수를 만드는 것이다. 이전 x는 더 이상 접근할 수 없다. mut은 같은 변수의 값을 바꾸는 것이다.
// mut은 타입을 바꿀 수 없다
let mut x = "hello";
// x = 5; // 컴파일 에러! 타입이 다름
// shadowing은 타입도 바뀐다
let x = "hello";
let x = 5; // OK — 새 변수
Ruby 개발자 입장에서 shadowing이 더 자연스럽게 느껴질 수 있다. 실제로 Rust 코드에서도 많이 쓴다. 특히 문자열을 파싱해서 숫자로 변환할 때 같은 이름을 재사용하는 패턴이 일반적이다.
const — Ruby의 CONSTANT
Ruby의 MAX_SIZE = 100처럼 상수를 선언할 때는 const를 쓴다.
const MAX_SIZE: usize = 100;
let과의 차이:
const는 반드시 타입을 명시해야 한다const는 컴파일 타임에 값이 결정된다 (런타임 계산 불가)const는 어디서든 쓸 수 있다 (함수 바깥도 OK)
Ruby에서는 상수에 재할당하면 경고만 나오고 실행은 된다. Rust에서는 const 재할당이 아예 불가능하다.
정리
| Ruby | Rust | 설명 |
|---|---|---|
x = 10 |
let x = 10; |
불변 |
x = 10; x = 20 |
let mut x = 10; x = 20; |
가변 |
x = "hi"; x = 42 |
let x = "hi"; let x = 42; |
shadowing |
MAX = 100 |
const MAX: usize = 100; |
상수 |
핵심 포인트
let은 불변이 기본 — Ruby의 freeze가 디폴트인 셈
mut을 붙이면 가변 — Ruby에서는 모든 변수가 암시적 mut
shadowing은 같은 이름으로 새 변수 생성 — 타입도 바꿀 수 있다
const는 컴파일 타임 상수 — Ruby의 CONSTANT보다 엄격
장점
- ✓ 불변 기본값 덕에 의도하지 않은 값 변경이 원천 차단된다
- ✓ shadowing으로 Ruby처럼 유연한 변수 재사용이 가능하다
단점
- ✗ 습관적으로 mut을 붙이게 되는데, 가능하면 불변으로 두는 게 Rust 관례
- ✗ shadowing 남용하면 같은 이름의 변수가 다른 값을 가져서 혼란