02. 변수·상수·섀도잉·타입 추론
Rust 의 변수는 기본적으로 불변(immutable) 입니다. mut 키워드로 명시해야 가변. 이런 디자인이 어떻게 멀티스레드 안정성과 코드 가독성을 동시에 끌어올리는지, 그리고 shadowing 이 어떻게 mut 없이도 값을 재사용하게 해 주는지 살펴봅니다. const 와 let 의 차이, 타입 어노테이션과 타입 추론도 함께 정리합니다.
이 강의에서 배우는 것
- 1let 으로 변수 바인딩을 만들고 기본이 불변임을 확인한다
- 2mut 를 붙여 가변 변수로 전환한다
- 3const 와 let 의 차이를 설명할 수 있다
- 4shadowing 을 활용해 타입을 바꿔가며 값을 재사용한다
- 5타입 어노테이션이 필요한 경우와 추론에 맡겨도 되는 경우를 구분한다
소개
다른 언어를 써 본 사람이 Rust 를 처음 만나서 가장 당황하는 지점이 이것입니다. `let x = 5; x = 6;` 가 컴파일 에러. 변수가 기본 불변이라는 것은 단순한 스타일 규칙이 아니라 **데이터 경합 방지의 출발점** 입니다. 멀티스레드에서 공유되는 값이 변경 불가하다는 사실이 컴파일 타임에 보장되면, 그것만으로 락의 절반은 필요 없어집니다.
핵심 개념
1) let — 변수 바인딩의 기본 형태
`let 이름 = 값;` 형태. 한 번 바인딩되면 재대입 불가.
let x = 5;
x = 6; // error[E0384]: cannot assign twice to immutable variable `x`2) mut — 가변으로 명시
`let mut 이름 = 값;` 형태. **선언 시점부터** 가변임을 명시해야 합니다.
let mut x = 5;
x = 6; // OK
println!("{}", x); // 63) const — 컴파일 타임 상수
- 반드시 **타입 명시** 필요: `const PI: f64 = 3.14;`
- 스코프 어디든 선언 가능 (모듈 최상단, 함수 안 등)
- 값은 컴파일 타임에 평가 가능한 식이어야 함 (런타임 함수 호출 불가)
- 관례상 **SCREAMING_SNAKE_CASE**
4) shadowing — 같은 이름으로 다시 바인딩
let 으로 같은 이름을 다시 선언하면 새 변수가 생기고 기존 것을 가립니다. **타입까지 바뀔 수 있다**는 것이 mut 와 결정적 차이.
let spaces = " ";
let spaces = spaces.len(); // &str -> usize 로 타입 변경 OK
println!("{}", spaces); // 3| 관점 | mut | shadowing |
|---|---|---|
| 같은 메모리 재사용 | ✓ | ✗ (새 바인딩) |
| 타입 변경 | ✗ | ✓ |
| 스코프 끝나면 원복 | ✗ | ✓ |
핵심 예제
타입 추론과 명시의 조합 — 정수 리터럴은 기본 i32 이지만 어노테이션으로 강제 가능.
fn main() {
let a = 10; // 추론: i32
let b: i64 = 10; // 명시: i64
let c = 10_u8; // 접미사 표기: u8
let pi: f64 = 3.14;
println!("a={}, b={}, c={}, pi={}", a, b, c, pi);
}shadowing 으로 입력 파싱 흐름을 깔끔하게:
fn main() {
let input = "42"; // &str
let input: i32 = input // shadow -> i32
.trim()
.parse()
.expect("숫자가 아닙니다");
println!("{}", input + 1); // 43
}자주 하는 실수
Q. mut 가 붙으면 변수의 타입도 바꿀 수 있나요?
A. 아닙니다. mut 는 **값** 만 바꿀 수 있고 타입은 그대로입니다. 타입을 바꾸려면 shadowing 을 쓰세요.
Q. const 와 let 의 차이가 뭔가요?
A. const 는 컴파일 타임에 평가되며 항상 타입 명시 필요. let 은 런타임에 평가 가능한 임의의 식. 또 const 는 절대 변경 불가(mut const 같은 건 없음).
Q. 변수가 불변이면 너무 불편하지 않나요?
A. 의외로 일상 코드 80% 는 한 번 만들고 그대로 쓰는 값입니다. 정말 변경이 필요할 때만 mut 를 붙이면 코드 의도가 명확해지고, 멀티스레드 코드에서 안전성도 올라갑니다.
정리
- let 은 기본 불변, 변경하려면 mut
- const 는 컴파일 타임 상수, 타입 명시 필수
- shadowing 은 새 바인딩이라 타입까지 바꿀 수 있다
- 타입 추론이 강력하지만, 가독성·명시성을 위해 어노테이션을 쓰는 경우도 흔하다
과제
- 불변 변수에 재대입을 시도해 에러 메시지를 확인하고, mut 를 붙여 고친다
- shadowing 을 활용해 `" 3.14 "` 같은 문자열을 f64 로 파싱하는 코드를 작성한다
- const 로 원주율을 정의하고 함수 안에서 참조해 본다