어떤 이름에 새 값을 대입할 수 있는지, 어디에서 그 이름을 쓸 수 있는지 정해 보세요. 반환받은 함수를 호출할 때마다 바뀌는 카운터도 출력으로 확인합니다.
첫 프로그램을 따라 토파즈를 설치하세요. let과 함수의 기본 사용법은 값과 함수에서 배울 수 있습니다.
카운터가 바뀌는 모습 보기
작업 폴더에 다음 코드를 bindings-scope-closures.tpz로 저장하세요.
function 카운터만들기(시작: int) -> () -> int {
let mut 현재 = 시작
() => {
현재 = 현재 + 1
현재
}
}
let 레이블 = "outer"
let 다음 = 카운터만들기(10)
{
let 레이블 = "inner"
print("{레이블}: {다음()}, {다음()}")
}
print(레이블)같은 폴더에서 파일을 검사하고 실행하세요.
topaz check bindings-scope-closures.tpz
topaz run bindings-scope-closures.tpz검사가 성공하면 종료 코드는 0입니다. 프로그램은 다음 두 줄을 출력합니다.
inner: 11, 12
outer두 번의 호출 따라가기
바인딩은 이름과 값을 연결합니다. let mut 현재 = 시작은 새 값을 대입할 수 있는 바인딩을 만듭니다. 카운터만들기(10)은 이 값을 10으로 시작하고 =>로 쓴 이름 없는 함수를 반환합니다. 이 함수를 람다라고 부릅니다.
람다는 바깥 함수의 현재에 계속 접근합니다. 이를 캡처라고 하며, 이런 접근을 유지하는 함수를 클로저라고 합니다. 카운터만들기가 반환한 뒤에도 다음에 이 클로저가 남습니다.
| 단계 | 현재의 값 | 반환값 |
|---|---|---|
카운터만들기(10) | 10 | 클로저 |
첫 다음() 호출 | 11 | 11 |
둘째 다음() 호출 | 12 | 12 |
두 호출은 같은 바인딩을 사용합니다. 둘째 호출은 첫 호출이 남긴 11에 1을 더합니다. 캡처한 바인딩은 클로저가 사용하는 동안 유지됩니다. 두 클로저가 같은 가변 바인딩을 캡처해도 바뀐 값을 함께 봅니다.
블록에서 사용하는 이름 찾기
스코프는 이름을 사용할 수 있는 코드 범위입니다. 토파즈는 렉시컬 스코프를 사용합니다. 이름의 선언 위치와 둘러싼 블록에 따라 어떤 바인딩을 가리키는지 정합니다.
안쪽의 let 레이블은 그 블록 안에서 바깥 바인딩을 가립니다. 이를 이름 가리기라고 합니다. 첫 print는 "inner"를 읽습니다. 블록 밖의 print(레이블)은 "outer"를 읽습니다. 같은 스코프에서 같은 이름을 두 번 선언하면 정적 오류입니다.
바인딩의 변경 여부 고르기
| 형태 | 사용할 때 | 재대입 |
|---|---|---|
const | 허용된 상수 표현식으로 컴파일 시점에 계산할 값 | 불가 |
let | 대입한 값을 유지할 이름 | 불가 |
let mut | 카운터처럼 새 값을 대입할 상태 | 가능 |
먼저 let으로 선언하고 재대입이 필요하면 mut를 붙이세요. 매개변수와 패턴으로 만든 이름은 불변입니다. 함수 안에서 매개변수의 값을 바꾸려면 새 let mut 바인딩에 담으세요. 예제의 카운터만들기도 시작을 이렇게 사용합니다.
현재의 선언에서 mut를 지우면 람다 안의 대입이 정적 오류가 됩니다. 카운터를 계속 사용하려면 bindings-scope-closures.tpz에서 let mut 현재로 되돌리세요. 캡처해도 원래 바인딩의 변경 가능 여부는 그대로입니다.
캡처와 선언 규칙
- 클로저는 생성하는 위치에서 볼 수 있는 이름을 캡처합니다. 나중에 선언한 이름이나 다른 형제 블록 안의 이름은 사용할 수 없습니다.
- 가변 선언에는 단일 식별자를 씁니다. 구조 분해 패턴으로 가변 바인딩을 만들 수 없습니다.
- 불변
let,const, 매개변수, 패턴 바인딩에는 클로저 안에서도 새 값을 대입할 수 없습니다. - 명시적 캡처 목록, 이동 캡처 문법, Rust식 클로저 트레이트, 빌림 수신자 문법은 지원하지 않습니다.
캡처한 바인딩의 수명이 파일을 닫는 시점을 정하지는 않습니다. File에 사용하는 using과 정리 규칙은 Defer와 리소스에서 확인하세요. 이름 있는 함수, 람다, 제네릭 함수의 선택은 함수와 제네릭에서 이어집니다.