What is the difference between .. and ..< in Kotlin, and which operator functions do they map to?
answer
- .. -> rangeTo, closed/inclusive
- ..< -> rangeUntil, half-open (excludes top)
- ..< stabilized in Kotlin 1.9, replaces until
- Use ..<size for index loops (no off-by-one)
- upper<lower -> empty range, no error
basics
~10 s1..10 includes both ends (1 through 10). 1..<10 excludes the top end (1 through 9). The first uses rangeTo, the second uses rangeUntil.
solid answer
~40 s`..` maps to the `rangeTo` operator and builds a **closed** range: `1..10` includes 1 and 10. `..<` maps to the `rangeUntil` operator and builds a **half-open** range: `1..<10` includes 1 through 9 but excludes 10. `..<` was stabilized in Kotlin 1.9 as a cleaner replacement for the older `until` infix function (`1 until 10`); both produce the same `IntRange` (an `IntRange` of 1..9), but `..<` reads better and pairs visually with `..`. The half-open form is ideal for index iteration: `for (i in 0..<list.size)` avoids the classic off-by-one you'd get from `0..list.size`. Under the hood both operators return concrete range types (`IntRange`, `LongRange`, `CharRange`) that implement `ClosedRange`/`OpenEndRange` and `Iterable`, so membership (`in`) and iteration both work.
code
kotlin · 5 linesval a = 1..10 // IntRange 1..10, includes 10
val b = 1..<10 // IntRange 1..9, excludes 10
println(10 in a) // true
println(10 in b) // false
println(b == (1 until 10)) // true (same range)go deeper
Knows .. is inclusive and ..< stops one short.
Maps both to rangeTo/rangeUntil and uses ..< to prevent index off-by-ones.
Knows ..< stabilized in 1.9 replacing until, and that an inverted range is empty rather than erroring.
Reasons about API ergonomics: half-open ranges as the safer default for indexing and how operator naming reduces a whole bug class.
## Two range operators Kotlin gives you two operators for building ranges: | Syntax | Operator function | Endpoints | Example values | |--------|-------------------|-----------|----------------| | `1..10` | `rangeTo` | both inclusive (closed) | 1,2,...,10 | | `1..<10` | `rangeUntil` | lower inclusive, upper **exclusive** (half-open) | 1,2,...,9 | `a..b` compiles to `a.rangeTo(b)`; `a..<b` compiles to `a.rangeUntil(b)`. ## Why ..< exists Before `..<`, the half-open range used the infix function `until`: ```kotlin for (i in 0 until list.size) { /* ... */ } // older style for (i in 0..<list.size) { /* ... */ } // 1.9+ preferred ``` `..<` (the `rangeUntil` operator) was stabilized in **Kotlin 1.9**. It is preferred because it visually mirrors `..` and reduces the chance of an off-by-one. `until` still works and produces an identical result. ## The off-by-one trap ```kotlin val list = listOf("a", "b", "c") for (i in 0..list.size) println(list[i]) // BUG: i reaches 3 -> IndexOutOfBounds for (i in 0..<list.size) println(list[i]) // correct: 0,1,2 ``` With a closed `..`, the upper bound `list.size` is itself a valid index in the loop, which overruns the array. The half-open `..<` stops one short. ## Underlying types Both operators on `Int` return an **`IntRange`**. `1..10` and `1..<11` are equal ranges. `IntRange` implements `ClosedRange<Int>` (and `OpenEndRange<Int>`) plus `Iterable<Int>`, which is why `in` (membership -> `contains`) and `for` iteration both work. `Char` and `Long` have their own `CharRange`/`LongRange`. ## Empty ranges If the upper bound is below the lower bound the range is **empty**: `5..1` and `5..<1` both have no elements, `isEmpty()` is true, and `x in emptyRange` is always false. There is no exception — it just iterates zero times. ## Key APIs/keywords - `rangeTo` (`..`), `rangeUntil` (`..<`), `until` (older infix) - `IntRange`, `ClosedRange`, `OpenEndRange` - `.isEmpty()`, `.first`, `.last`, `.contains()`
- Is 1..<10 the same as 1 until 10?Yes, both produce the IntRange 1..9. `..<` (rangeUntil, Kotlin 1.9+) is the preferred operator form; `until` is the older infix function.
- What happens with 5..1?It is an empty range: isEmpty() is true, it iterates zero times, and `x in 5..1` is always false. No exception is thrown.
.. is a fence that includes both posts; ..< includes the near post but stops just before the far one.
saying these in an interview costs you the question
- Saying ..< includes the upper bound
- Using 0..list.size for index loops (off-by-one crash)
- Claiming 5..1 throws an exception
- Not knowing ..< maps to rangeUntil
- Thinking until and ..< produce different ranges