📌

변수와 불변성 — 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; 상수

핵심 포인트

1

let은 불변이 기본 — Ruby의 freeze가 디폴트인 셈

2

mut을 붙이면 가변 — Ruby에서는 모든 변수가 암시적 mut

3

shadowing은 같은 이름으로 새 변수 생성 — 타입도 바꿀 수 있다

4

const는 컴파일 타임 상수 — Ruby의 CONSTANT보다 엄격

장점

  • 불변 기본값 덕에 의도하지 않은 값 변경이 원천 차단된다
  • shadowing으로 Ruby처럼 유연한 변수 재사용이 가능하다

단점

  • 습관적으로 mut을 붙이게 되는데, 가능하면 불변으로 두는 게 Rust 관례
  • shadowing 남용하면 같은 이름의 변수가 다른 값을 가져서 혼란

사용 사례

불변 데이터를 다룰 때 — 설정값, 계산 결과 등 타입 변환 시 shadowing — parse 결과를 같은 이름으로