1. Overview

In this tutorial, we’ll discuss the usage of structural jump expressions in Kotlin.

Simply put, Kotlin has three structural jump expressions: return, break, continue. In the next sections, we’ll cover their functionalities with and without a label.

2. Labels in Kotlin

Any expressions in Kotlin can be marked with a label.

We create a label by using an identifier followed by the “@” sign. For example, abc@, loop@ are valid labels.

To label an expression, we simply add the label in front of it:

loop@ for (i in 1..10) {
    // some code 
}

3. The Break Statement

Without a label, break terminates the nearest enclosing loop.

Let’s have a look at an example:

@Test
fun givenLoop_whenBreak_thenComplete() {
    var value = ""
    for (i in "hello_world") {
        if (i == '_') break
        value += i.toString()
    }
    assertEquals("hello", value)
}

Alternatively, we can use break with a label, which terminates the loop marked with that label:

@Test
fun givenLoop_whenBreakWithLabel_thenComplete() {
    var value = ""
    outer_loop@ for (i in 'a'..'d') {
        for (j in 1..3) {
            value += "" + i + j
            if (i == 'b' && j == 1)
                break@outer_loop
        }
    }
    assertEquals("a1a2a3b1", value)
}

In this case, the outer loop is terminated when the i and j variables equal “b” and “1” respectively.

4. The Continue Statement

Next, let’s have a look at the continue keyword, which we can also use with or without a label.

Without a label, continue will proceed to the next iteration of the enclosing loop:

@Test
fun givenLoop_whenContinue_thenComplete() {
    var result = ""
    for (i in "hello_world") {
        if (i == '_') continue
        result += i
    }
    assertEquals("helloworld", result)
}

On the other hand, when we use continue with a label marking a loop, it will proceed to the next iteration of that loop:

@Test
fun givenLoop_whenContinueWithLabel_thenComplete() {
    var result = ""
    outer_loop@ for (i in 'a'..'c') {
        for (j in 1..3) {
            if (i == 'b') continue@outer_loop
            result += "" + i + j
        }
    }
    assertEquals("a1a2a3c1c2c3", result)
}

In this example, we’ve used continue to skip one iteration of the loop labeled outer_loop.

5. The Return Statement

Without a label, it returns to the nearest enclosing function or anonymous function:

@Test
fun givenLambda_whenReturn_thenComplete() {
    var result = returnInLambda();
    assertEquals("hello", result)
}

private fun returnInLambda(): String {
    var result = ""
    "hello_world".forEach {
        if (it == '_') return result
        result += it.toString()
    }
    //this line won't be reached
    return result;
}

Return is also useful when we want to apply continue logic on anonymous functions:

@Test
fun givenAnonymousFunction_return_thenComplete() {
    var result = ""
    "hello_world".forEach(fun(element) {
        if (element == '_') return
        result += element.toString()
    })
    assertEquals("helloworld", result)
}

In this example, the return statement will return to the caller of the anonymous fun, i.e. the forEach loop.

In the case of a lambda expression, we can also use return with a label to achieve a similar result:

@Test
fun givenLambda_whenReturnWithExplicitLabel_thenComplete() {
    var result = ""
    "hello_world".forEach lit@{
        if (it == '_') {
            return@lit
        }
        result += it.toString()
    }
    assertEquals("helloworld", result)
}

Alternatively, we can also return using an implicit label:

@Test
fun givenLambda_whenReturnWithImplicitLabel_thenComplete() {
    var result = ""
    "hello_world".forEach {
        if (it == '_') {
            // local return to the caller of the lambda, i.e. the forEach loop
            return@forEach
        }
        result += it.toString()
    }
    assertEquals("helloworld", result)
}

In the example above, the return statement will also return to the caller of the lambda – the forEach loop.

Finally, return can be used with a label to apply break logic to lambda expressions by returning to a label outside:

@Test
fun givenAnonymousFunction_returnToLabel_thenComplete() {
    var result = ""
    run loop@{
        "hello_world".forEach {
            if (it == '_') return@loop
            result += it.toString()
        }
    }
    assertEquals("hello", result)
}

6. Conclusion

In this article, we have gone through the use cases of return, break, continue in Kotlin.

The sample code could be found over on GitHub.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.