What does the `tailrec` modifier do in Kotlin, and why would you use it?
answer
- tailrec = recursion rewritten to a loop
- Call must be the LAST operation (tail position)
- Avoids StackOverflowError, constant stack space
- Not last? compiler warns, falls back to plain recursion
- Accumulator parameter makes the call tail
basics
~10 stailrec tells the compiler to turn a function that calls itself at the very end into a plain loop. This avoids the program crashing with a stack overflow on deep recursion.
solid answer
~40 s`tailrec` is a function modifier that asks the Kotlin compiler to perform tail-call optimization (TCO): it rewrites a self-recursive function whose recursive call is the last operation into an iterative loop. Normal recursion pushes a new stack frame per call, so deep recursion throws `StackOverflowError`; the `tailrec` version reuses a single frame, running in constant stack space. The recursive call must be in tail position — its result is returned directly with nothing done to it afterward. If the compiler cannot apply the optimization (e.g. the call isn't the last operation), it emits a warning and the function stays ordinary recursion. You typically use it for iterative algorithms expressed recursively: computing a fixed point, walking a linked structure, or accumulator-style loops.
code
kotlin · 6 linestailrec fun sum(n: Long, acc: Long = 0): Long =
if (n == 0L) acc else sum(n - 1, acc + n)
fun main() {
println(sum(1_000_000)) // no StackOverflowError
}go deeper
Knows tailrec turns end-recursion into a loop to avoid StackOverflowError.
Can explain tail position and rewrite a function with an accumulator to satisfy it.
Discusses the compiler warning-only fallback and the direct-self-recursion limit.
Frames it as a readability vs. safety trade-off and knows the bytecode is a plain loop with no runtime cost.
## What `tailrec` is `tailrec` is a Kotlin **function modifier** (a keyword you put before `fun`) that requests **tail-call optimization (TCO)**. The compiler rewrites a self-recursive function into an ordinary `while` loop. ## Why it matters: the stack Every function call pushes a **stack frame** (space for parameters and locals) onto the call stack. The JVM stack is finite, so a function that recurses thousands of times eventually throws **`StackOverflowError`**. A **tail call** is a recursive call that is the **last thing** the function does — its result is returned directly with no further work. When that's the case, the current frame is no longer needed, so the compiler can **reuse the same frame** instead of allocating a new one. The result runs in **constant stack space**, exactly like a hand-written loop. ## The rule: the call must be in tail position The recursive call must be the final operation. These are NOT tail calls and will defeat the optimization: - `return n * factorial(n - 1)` — the multiply happens *after* the recursive call. - `return factorial(n - 1) + 1` — addition after the call. - A recursive call inside a `try`/`catch` block — the runtime must keep the frame to handle exceptions. The fix is usually an **accumulator parameter** that carries the running result, so the call really is last. ```kotlin tailrec fun factorial(n: Long, acc: Long = 1): Long = if (n <= 1) acc else factorial(n - 1, acc * n) // last operation -> tail call ``` ## What the compiler does if it can't optimize If you mark a function `tailrec` but the call is not in tail position, the compiler emits a **warning** ("A function is marked as tail-recursive but no tail calls are found" or that the call is not a tail call) and compiles it as **ordinary recursion** — it does not error out, but you lose the protection. ## Limits - Only **direct self-recursion** is supported — function A calling itself, not mutual recursion (A calls B calls A). - It cannot be combined with `open` (an overridable open function can't be safely optimized). - Each recursive call site must be in tail position. ## Mental model Think of `tailrec` as syntactic sugar: you write clear recursion, the compiler hands you a loop.
- What happens if you mark a function `tailrec` but the recursive call is not in tail position?The compiler emits a warning and compiles it as ordinary recursion, so it can still throw StackOverflowError.
- Does `tailrec` work for mutual recursion (A calls B calls A)?No — only direct self-recursion is optimized; mutual recursion is not supported.
Like reusing the same desk for each step of a long calculation instead of fetching a brand-new desk every time and stacking them to the ceiling.
saying these in an interview costs you the question
- Claiming `tailrec` makes any recursion safe regardless of where the call is
- Saying it throws a compile error (it only warns) when the call isn't tail
- Confusing it with `inline` or with general performance optimization
- Believing it works across two mutually-recursive functions