3.7. 범위 표현식
범위 표현식은 .. 또는 ..= 토큰 (구두점과 연산자 토큰) 으로 시작해서 시작과 끝을 가지는 범위 값을 만들어 줍니다.
range_expr ::= expr ".." expr // half-open
| expr "..=" expr // inclusive
| ".." expr // from start
| "..=" expr // from start, inclusive
| expr ".." // unbounded end
| ".." // full
3.7.1. 여섯 가지 형태
범위 표현식은 다음과 같이 여섯 가지 형태로 쓸 수 있습니다.
0..10 // [0, 1, ..., 9] 배제 끝
0..=10 // [0, 1, ..., 10] 포함 끝
..10 // 0..10 과 동일 시작 생략 (전위)
..=10 // 0..=10 과 동일 시작 생략, 포함 끝
10.. // [10, 11, ...] 끝 생략, 무한
.. // 전체 양쪽 생략, 주로 슬라이싱에서
각 형태의 의미를 정리하면, .. 는 끝을 배제하는 반열림 구간을 만들고, ..= 는 끝을 포함합니다. 시작을 생략하면 0 이나 자료에 따른 기본값으로 처리되고, 끝을 생략하면 무한 또는 자료의 끝까지로 처리됩니다.
3.7.2. desugar
각 형태는 다음과 같이 표준 라이브러리 타입으로 desugar 됩니다.
a .. b → Range::new(a, b)
a ..= b → RangeInclusive::new(a, b)
.. b → RangeTo::new(b)
..= b → RangeToInclusive::new(b)
a .. → RangeFrom::new(a)
.. → RangeFull
이 변환에 쓰이는 정확한 표준 라이브러리 트레잇과 타입은 라이브러리 사양에서 정의합니다. 본 문서는 언어가 여섯 형태를 별개의 표현식으로 인식하고 표준 라이브러리 타입의 인스턴스로 desugar 한다는 사실까지만 책임집니다.
3.7.3. 우선순위
우선순위와 결합성 의 등급에서 범위 연산자 (.., ..=) 는 prec=0 비결합으로, null 합체 (??, prec=1) 를 포함한 모든 이항 연산자보다 낮고 대입보다만 높습니다.
0..n + 1 → 0 .. (n + 1) // + 가 .. 보다 강함
a ?? 0..3 → (a ?? 0) .. 3 // ?? 가 .. 보다 강함
a..b..c // ERROR: 범위 연산자는 비결합
이항 연산자 가운데 가장 약하게 묶이기 때문에, 시작과 끝에 산술 표현식을 넣어도 자연스럽게 읽힙니다.
호출 인자 안의 ..x
호출 인자 자리의 ..x 는 범위가 아니라 spread (4.2.6) 입니다. 그래서 f(..5) 는 5 를 펼치는 호출이며, RangeTo 를 넘기려면 괄호로 감쌉니다 (f((..5))). ..=x 는 spread 가 아니므로 인자 자리에서도 범위입니다 (f(..=5) 는 RangeToInclusive 를 넘김).
3.7.4. 사용처
반복
범위 값은 주로 반복문에서 사용됩니다.
for i in 0..10 { ... }
이 경우 범위 값을 루프 표현식 의 for 루프가 받아서 반복합니다. Range 와 RangeInclusive 가 trait 의 Iterator 트레잇을 구현하고 있으면 자동으로 반복됩니다.
슬라이싱과는 별개
슬라이싱과 범위 표현식은 서로 다른 표기라는 점을 유의해야 합니다.
arr[1:3] // [인덱싱과 슬라이싱](306-indexing-and-slicing.md) 의 슬라이스, 콜론 (`:`) 사용
0..3 // 범위 값, 점 둘 (`..`) 사용
두 표기는 서로 다른 토큰이고 다른 의미를 가집니다. 슬라이싱은 인덱스 표현식의 내부 형태이고, 범위는 값 (Range 타입 인스턴스) 입니다.
패턴 매칭
범위 패턴 (패턴) 도 같은 토큰을 사용합니다. 의미는 그 자리에서 정의됩니다.
match x {
1..=5 => ...,
_ => ...,
}
3.7.5. step 은 메서드로
step (보폭) 은 표기 안에 두지 않습니다. 대신 표준 라이브러리의 .step_by(n) 메서드로 처리합니다.
(0..100).step_by(2) // [0, 2, 4, ..., 98]
이 결정은 rok 의 일반적인 방향 (언어 syntax 는 단순하게 유지하고, 부가 기능은 메서드로 제공) 과 일관됩니다. 슬라이싱의 [::2] 는 별개 표기이며, 그 자리에는 step 이 자연스러워서 도입했습니다.
3.7.6. 역방향
반대 방향으로 반복하고 싶을 때는 .rev() 메서드를 사용합니다.
(0..10).rev()
범위 표기 자체에 음수 step 을 두지는 않습니다.
3.7.7. 빈 범위
시작이 끝보다 크면 빈 범위가 됩니다.
5..3 // 빈 범위, 정상 표현, 반복 시 0회
5..=3 // 빈 범위
이 경우 컴파일 에러가 아니라 반복 0회인 정상 값으로 처리됩니다. 0회 반복이 자연스러운 의미입니다.
3.7.8. 무한 범위의 사용
0.. 같은 열린 범위로 for 를 돌 수 있으며, 루프 안의 break 로 끝냅니다. 값이 타입의 최댓값을 넘으면 산술 overflow 와 같이 다룹니다 (검사가 켜져 있으면 panic).
for i in 0.. {
if condition(i) { break; }
}
무한 반복 자체는 loop { ... } 와 등가이며, 사용 편의 차이만 있습니다.