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.