Rust 스코프
스코프(scope)는 변수가 유효한 코드 영역이에요. 간단한 개념이지만, 이걸 이해하면 Rust의 다른 규칙들(특히 소유권)이 훨씬 잘 이해돼요.
스코프는 중괄호로 정해져요
섹션 제목: “스코프는 중괄호로 정해져요”변수는 선언된 지점부터, 그 변수가 속한 블록의 닫는 }까지 존재해요.
fn main() { let x = 5; // 여기서 x가 스코프에 들어와요
{ let y = 10; // 여기서 y가 스코프에 들어와요 println!("x = {x}, y = {y}"); // 둘 다 유효해요 } // 여기서 y가 스코프를 벗어나요
println!("x = {x}"); // println!("{y}"); // ❌ 에러: y는 더 이상 스코프에 없어요}실행 결과:
x = 5, y = 10x = 5y는 안쪽 { } 블록 안에서만 존재해요. 그 블록이 끝나면 y는 사라져요. 그 뒤에 y를 쓰려고 하면 실행 중에 깜짝 놀라는 게 아니라 컴파일 에러가 나요.
스코프는 한 방향으로만 통해요. 안쪽 블록은 자신을 둘러싼 바깥 블록의 변수를 볼 수 있지만(그래서 안쪽 블록에서 x를 출력할 수 있었어요), 바깥 블록은 안쪽 블록 속을 볼 수 없어요.
함수 본문도 스코프예요
섹션 제목: “함수 본문도 스코프예요”매개변수와 함수 안에서 만든 변수는 그 함수가 호출되어 실행되는 동안에만 존재해요.
fn calculate() -> i32 { let result = 5 * 2; // result는 calculate 안에서만 존재해요 result}
fn main() { let value = calculate(); println!("{value}"); // println!("{result}"); // ❌ 에러: 여기에는 result가 없어요}실행 결과:
10반대 방향도 마찬가지예요. 함수는 main의 지역 변수를 볼 수 없어요. 값이 필요하다면 함수 페이지에서 본 것처럼 매개변수로 넘겨줘야 해요.
블록도 표현식이에요
섹션 제목: “블록도 표현식이에요”{ } 블록은 변수가 살 수 있는 범위를 제한할 뿐만 아니라 값을 만들어 낼 수도 있어요. 그 값은 블록 안의 마지막 표현식, 즉 세미콜론 없이 쓴 줄이에요. 함수가 마지막 줄을 반환하게 만드는 것과 같은 규칙이에요.
무언가를 계산하는 데 보조 변수가 몇 개 필요하지만, 계산이 끝난 뒤에는 그 변수들이 남아 있지 않았으면 할 때 편리해요.
fn main() { let price = 40;
let total = { let tax = price / 10; // tax는 이 블록 안에서만 살아 있어요 let shipping = 5; price + tax + shipping // 세미콜론 없음: 이게 블록의 값이에요 };
println!("total = {total}"); // println!("{tax}"); // ❌ 에러: tax는 스코프를 벗어났어요}실행 결과:
total = 49주의할 점이 두 가지 있어요.
- 마지막 줄
price + tax + shipping에는 세미콜론이 없어요. 세미콜론을 붙이면 블록이 숫자 대신()(아무것도 없음)를 돌려주고,println!은 그걸 출력할 수 없어요. - 전체가 하나의
let구문이라서, 닫는 중괄호 뒤에};로 끝나요.
블록이 끝나면 tax와 shipping은 사라지고 결과인 total만 남아요. 이렇게 보조 변수를 작은 블록 안에 두면, 그 변수들이 이 계산 하나에만 필요하다는 게 분명해져요.
스코프가 소유권에 중요한 이유
섹션 제목: “스코프가 소유권에 중요한 이유”이 내용은 조금 뒤에 배울 소유권과 바로 연결돼요. 값은 그 값의 소유자의 스코프가 끝나는 순간 자동으로 정리돼요.
fn main() { { let text = String::from("안녕하세요"); println!("{text}"); } // 여기서 text의 스코프가 끝나요. Rust가 자동으로 해제(drop)해요}실행 결과:
안녕하세요Rust에 가비지 컬렉터가 필요 없는 이유가 바로 이거예요. 모든 스코프가 정확히 어디서 끝나는지 컴파일할 때 이미 알고 있거든요.
섀도잉과 스코프
섹션 제목: “섀도잉과 스코프”변수 페이지에서 배운 섀도잉(shadowing)을 기억하나요? 스코프 안에서 같은 이름으로 let을 쓸 때마다 별개의 새 변수가 만들어져요. 바깥 변수를 지우는 게 아니라, 그 스코프가 끝날 때까지만 대신 자리를 차지하는 거예요.
fn main() { let x = 5; { let x = x * 2; // 이 블록 안에서만 쓰이는 새로운 x println!("안쪽 x = {x}"); // 10 } println!("바깥 x = {x}"); // 5: 영향을 받지 않아요}실행 결과:
안쪽 x = 10바깥 x = 5연습 문제
섹션 제목: “연습 문제”1. 스코프 에러 고치기
섹션 제목: “1. 스코프 에러 고치기”이 프로그램은 컴파일되지 않아요. 블록이 끝난 뒤에 area를 쓰고 있기 때문이에요.
fn main() { let width = 3; { let height = 4; let area = width * height; } println!("area = {area}");}height는 계속 블록 안에서만 살아 있게 하면서, area = 12를 출력하도록 고쳐 보세요. 힌트: 블록이 넓이를 만들어 내게 하고, 그 결과를 블록 바깥에서 선언한 변수에 담으세요.
정답 보기
fn main() { let width = 3; let area = { let height = 4; width * height }; println!("area = {area}");}실행 결과:
area = 12이제 area는 main의 스코프에 있으니까 println!이 볼 수 있어요. height는 여전히 블록 안에서만 필요하고, 블록이 끝날 때 해제돼요.
2. 중첩 단계
섹션 제목: “2. 중첩 단계”level 변수를 1로 만드세요. 블록을 하나 열어서 2로 섀도잉하고, 그 안에 블록을 하나 더 열어서 3으로 섀도잉하세요. 각 블록 안에서 level을 출력하고, 각 블록이 닫힌 직후에도 다시 출력해 보세요. 힌트: 숫자가 1, 2, 3으로 올라갔다가 다시 2, 1로 내려와야 해요.
정답 보기
fn main() { let level = 1; println!("레벨 {level}"); { let level = 2; println!("레벨 {level}"); { let level = 3; println!("레벨 {level}"); } println!("다시 레벨 {level}"); } println!("다시 레벨 {level}");}실행 결과:
레벨 1레벨 2레벨 3다시 레벨 2다시 레벨 1안쪽의 level은 자신의 블록이 끝날 때까지만 바깥의 level을 가려요. 바깥 변수는 한 번도 바뀌지 않았기 때문에, 그대로 남아서 기다리고 있어요.
- 스코프는 변수가 유효한 영역이고,
{ }로 표시돼요. - 변수는 스코프의 닫는
}에 도달하는 순간 사라져요. - 함수 본문도 스코프예요. 함수 안의 지역 변수는 함수가 반환될 때 사라져요.
- 블록은 표현식이에요. 마지막 줄(세미콜론 없음)이 블록의 값이 되고, 보조 변수는 블록이 끝날 때 사라져요.
- 스코프 덕분에 Rust는 값을 언제 해제할지 정확히 알아요. 그래서 가비지 컬렉터가 필요 없어요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. 이 페이지에 따르면 Rust에 가비지 컬렉터가 필요 없는 이유는 무엇일까요?
2. 안쪽 블록에서 let x = x * 2;로 변수를 섀도잉하면 어떻게 될까요?
3. let total = { let a = 2; a * 10 }; 다음에 total의 값은 무엇일까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- 변수의 스코프 — The Rust Programming Language 한국어판: 소유권으로 가는 첫걸음으로 스코프가 어떻게 동작하는지 설명해요.
- Scope and Shadowing — Rust by Example (영어): 블록과 섀도잉을 보여 주는 짧은 예제를 실행해 볼 수 있어요.
- RAII — Rust by Example (영어): 스코프가 끝날 때 값에 무슨 일이 일어나는지 다뤄요. 다음 단계로 넘어갈 준비가 되었을 때 읽어 보세요.
- Block expressions — The Rust Reference (영어): 블록과 블록이 만드는 값에 대한 정확한 규칙이에요.