ProgrammingBackend developer

Describe the mechanism of numeric ranges in Kotlin: how do Range and Progression work, how to create custom ranges, and in which scenarios are they convenient to use?

Pass interviews with Hintsage AI assistant

Answer

Background

From the very beginning, Kotlin introduced ranges (Range) to simplify operations with sequential sets of values — this is inherited from languages with concise syntax when working with numbers (e.g., Python). Additionally, the Range mechanism was extended to Progression, allowing for step iteration and supporting different types of numbers and characters.

Problem

Iterating over integers, letters, timestamps, etc. often requires special syntax and reliable support in the standard library; otherwise, the code becomes bulky, unreadable, and prone to boundary errors.

Solution

Kotlin has standard types IntRange, CharRange, and LongRange, as well as Progression for stepped iteration. Furthermore, you can define ranges for arbitrary comparable types.

// Simple range for (i in 1..5) print(i) // 12345 // Range with step for (i in 1..10 step 2) print(i) // 13579 // Reverse range for (i in 5 downTo 1) print(i) // 54321 // Custom range (e.g., for Version) data class Version(val major: Int, val minor: Int): Comparable<Version> { override fun compareTo(other: Version): Int = compareValuesBy(this, other, Version::major, Version::minor) } operator fun ClosedRange<Version>.iterator(): Iterator<Version> = object : Iterator<Version> { var current = start override fun hasNext() = current <= endInclusive override fun next() = current.also { current = Version(current.major, current.minor + 1) } } val v1 = Version(1, 0) val v2 = Version(1, 3) for (v in v1..v2) println(v)

Key features:

  • Standard range syntax: .., downTo, until, step
  • Working with numeric, character, and user-defined types
  • Application in loops, membership tests, partitioning, validation

Tricky questions.

What is the difference between the expression 1..5 and 1 until 5?

1..5 includes both ends of the range: 1,2,3,4,5. 1 until 5 does not include the last element: 1,2,3,4.

Can a range with a step less than zero be defined using step?

No. For decreasing ranges, use downTo, then step: 5 downTo 1 step 2 (you get 5,3,1).

Can ranges be used with types that do not implement Comparable?

No. To create a range, the type must support comparison. Otherwise, the compiler will not allow the definition.

Common errors and anti-patterns

  • Using until instead of .. or vice versa, confusion with inclusivity of bounds
  • Specifying a negative step for an increasing range (step does not make the range reverse)
  • Not adhering to Comparable requirements for user-defined types in a range

Real-life example

Negative case

In the code, instead of 1 until n+1, 1..n was used. This results in an extra element, and the loop exceeds the permissible range.

Pros:

  • The edge case is unintentionally captured.

Cons:

  • Errors in partitioning, incorrect sum calculations at the boundary.

Positive case

Using for (i in 0 until n) for indexing an array of length n, the range exactly matches the allowable index values.

Pros:

  • Errors of going out of bounds of the array are excluded.
  • Readability is enhanced.

Cons:

  • It is necessary to remember the difference between .. and until when switching between languages.