Rust에서 변수의 범위는 블록(중괄호 {})으로 제한됩니다. 현재 또는 외부 범위에 이미 존재하는 이름과 동일한 이름으로 새로운 변수를 선언하는 것을 섀도잉(shadowing)이라고 합니다. 섀도잉은 불변성 개념을 훼손하지 않고 변수의 타입과 값을 변경할 수 있게 해줍니다.
예:
let x = 5; // x: i32 = 5 let x = x + 1; // x: i32 = 6 (이전 x를 섀도잉) let x = "hello"; // x: &str = "hello" (타입 변경)
섀도잉은 성능에 영향을 미치지 않습니다(이건 단순히 구문 구조로, LLVM IR 수준에서 서로 다른 변수로 컴파일됩니다), 하지만 코드의 가독성과 유지 관리에 크게 악영향을 미칠 수 있으며 논리적 오류의 위험을 증가시킵니다.
질문: 변수를 섀도잉하면 이전 변수가 메모리 소유권을 가질 경우 자원이 해제되나요(예: String)?
자주 하는 대답: 아니요, 변수는 단순히 "재정의"될 뿐입니다.
정답: 네, 변수가 섀도잉되면 이전 값이 범위를 벗어나고, 만약 그것이 자원을 소유하고 있었다면 Drop이 호출됩니다. 이는 메모리의 올바른 해제를 보장합니다.
예:
let s = String::from("abc"); let s = 5; // 여기서 String이 해제됩니다(Drop 호출됨)
이야기
한 젊은 개발자가 열린 파일 디스크립터를 가진 구조체에서 섀도잉을 사용하다가 의도치 않게 변수를 예상보다 일찍 섀도잉하여 작업이 끝나기 전에 파일을 닫게 되었습니다. 이로 인해 일부 경우에 데이터 손실이 발생했습니다.
이야기
한 연구 프로젝트에서 섀도잉이 동일한 중첩 수준의 변수에 대해 사용되었고, 그로 인해 개발자들이 자주 혼동하여 "예전" 변수 값을 최신으로 생각하고 접근하는 일이 발생했습니다.
이야기
하나의 대규모 서버 애플리케이션에서 구조체의 필드와 메소드 내부의 변수를 암시적으로 섀도잉하여 필드가 변경되지 않고 동일한 이름의 새로운 로컬 변수가 변경되는 바람에 불명확한 오류가 발생했습니다. 이 오류는 시스템의 잘못된 동작 원인을 오랜 시간 동안 찾아야만 발견되었습니다.