언어 기초

바인딩·스코프·클로저

불변 바인딩과 가변 바인딩의 선택 기준을 다루고 렉시컬 스코프와 클로저의 가변 셀 공유 방식을 설명합니다.

바인딩은 값에 이름을 부여하는 작업입니다. 선언 시점에 두 가지 요소가 결정됩니다. 하나는 해당 이름에 새 값을 재대입할 수 있는지 여부이고, 다른 하나는 코드 내에서 해당 이름이 참조되는 유효 범위입니다. 클로저는 외부 함수 실행이 끝난 후에도 필요한 바인딩을 유지할 수 있습니다.

캡처한 바인딩이 바뀌는 모습 보기

다음 코드를 bindings-scope-closures.tpz로 저장합니다.

TOPAZ
function 카운터만들기(시작: int) -> () -> int {
    let mut 현재 = 시작
    () => {
        현재 = 현재 + 1
        현재
    }
}

let 레이블 = "outer"
let 다음 = 카운터만들기(10)

{
    let 레이블 = "inner"
    print("{레이블}: {다음()}, {다음()}")
}

print(레이블)

검사한 다음 실행합니다.

BASH
topaz check bindings-scope-closures.tpz
topaz run bindings-scope-closures.tpz

두 번의 호출은 하나의 카운터를 공유합니다. 블록 내부의 레이블은 블록이 끝날 때 소멸합니다.

출력
inner: 11, 12
outer

복사된 값이 아니라 바인딩 따라가기

카운터만들기는 가변 바인딩 현재를 생성한 뒤 람다를 반환합니다. 람다는 현재를 사용하므로 이 바인딩을 캡처합니다. 원본 선언이 let mut이므로 람다 내부에서 새 값을 재대입할 수 있습니다.

클로저는 초기값 10을 복사해 두지 않고 단일 가변 저장 공간을 지속적으로 참조합니다. 이 저장 공간을 셀이라고 부릅니다. 첫 번째 호출은 셀 값을 11로 변경하고, 두 번째 호출은 그 값을 읽어 12로 변경합니다. 카운터만들기 실행이 끝난 후에도 다음이 이 셀을 참조하므로 셀은 유지됩니다.

레이블은 렉시컬 스코프를 보여 줍니다. 내부 블록은 외부 이름을 가리고 자체 바인딩을 생성할 수 있습니다. 블록 내부에서 레이블"inner"를 가리킵니다. 블록을 벗어나면 내부 바인딩의 스코프가 종료되어 외부의 "outer"가 다시 참조됩니다. 동일한 스코프에서 같은 이름을 두 번 선언하면 정적 오류가 발생합니다.

바인딩을 의도적으로 고르기

형태사용할 때재대입
const제한적 상수 표현식으로 계산되는 컴파일 시점 값불가
let바인딩을 변경하지 않는 일반적인 런타임 값불가
let mut재대입 자체가 모델의 일부인 상태가능

기본 선택은 let입니다. 추후 대입하는 동작이 의미가 있을 때만 mut를 붙이세요. 그러면 어떤 상태가 변하는지 선언 위치에서 바로 파악할 수 있습니다.

매개변수와 패턴이 생성한 이름은 불변입니다. 함수 내부에서 변경되는 상태가 필요하면 매개변수 값을 새로운 let mut 바인딩에 담으세요. 그리고 그 상태를 잘 설명하는 이름을 붙이세요.

흔한 실수: 캡처를 스냅샷으로 생각하기

불변 바인딩을 캡처하면 클로저 내부에서도 읽기 전용입니다. 가변 바인딩을 캡처하면 유효한 셀을 그대로 공유합니다. 따라서 한 클로저가 변경한 값을 동일한 바인딩의 다음 읽기 작업에서 확인할 수 있습니다. 두 클로저가 하나의 가변 바인딩을 캡처하더라도 카운터가 따로 생성되지 않고 같은 셀을 공유합니다.

클로저를 생성하는 시점에도 스코프 규칙은 동일하게 적용됩니다. 해당 위치에서 참조할 수 있는 이름만 캡처 가능하며, 이후에 선언되는 이름이나 인접 블록 내부의 바인딩에는 접근할 수 없습니다.

정확한 한계

  • 내부 스코프는 외부 이름을 가릴 수 있습니다. 다만 단일 스코프에서 동일한 이름을 두 번 선언할 수는 없습니다.
  • 불변 let·const·매개변수·패턴 바인딩에는 새 값을 재대입할 수 없습니다.
  • 가변 선언에는 단일 식별자만 사용하며, 구조 분해 패턴은 사용할 수 없습니다.
  • 명시적 캡처 목록, 이동 캡처 문법, Rust식 클로저 트레이트, 빌림 수신자 문법은 지원하지 않습니다.
  • 리소스 수명 관리는 별도의 영역입니다. File 전용 using과 결정론적 정리는 Defer와 리소스에서 다룹니다.

불변 바인딩과 가변 바인딩을 처음 다루는 위치는 값과 함수입니다. 명명된 함수와 람다, 제네릭 함수 중 무엇을 선택할지는 함수와 제네릭에서 이어서 살펴보세요.