80

In the code below, I want to show my empty views if trips is empty and then return and avoid running the below code, but the compiler says "return is not allowed here".

mainRepo.fetchUpcomingTrips { trips ->
    if (trips.isEmpty()) {
        showEmptyViews()
        return
    }

    // run some code if it's not empty
}

Is there a way to return like that?

I know I can just put it in an if else block but I hate writing if else's, it's less understandable/readable in my opinion when there's a few more conditions.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
JozeRi
  • 3,219
  • 6
  • 27
  • 45

6 Answers6

116

Just use the qualified return syntax: return@fetchUpcomingTrips.

In Kotlin, return inside a lambda means return from the innermost nesting fun (ignoring lambdas), and it is not allowed in lambdas that are not inlined.

The return@label syntax is used to specify the scope to return from. You can use the name of the function the lambda is passed to (fetchUpcomingTrips) as the label:

mainRepo.fetchUpcomingTrips { trips ->
    if (trips.isEmpty()) {
        showEmptyViews()
        return@fetchUpcomingTrips 
    }

    // ...
}

Related:

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • 2
    Why is return not allowed in a lambda though? I don't see why it would be explicitly forbidden. – Emil S. Aug 25 '19 at 18:52
  • 5
    @EmilS. A non-inlined lambda may be called not in-place but wrapped into an instance passed to some other function and then called, maybe even on a different thread. In that case, a non-qualified `return` (which means 'return from the nearest `fun`') couldn't work. – hotkey Aug 25 '19 at 19:00
26

You can also replace { trips -> with fun(trips) { to form an anonymous function which can use return normally.

Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
8

Plain return suggests that you return from the function. Since you can't return from the function inside a lambda, the compiler will complain. Instead, you want to return from the lambda, and you have to use a label:

mainRepo.fetchUpcomingTrips { trips ->
    if (trips.isEmpty()) {
        showEmptyViews()
        return@fetchUpcomingTrips
    }

    //run some code if it's not empty
}
Ilya
  • 21,871
  • 8
  • 73
  • 92
nhaarman
  • 98,571
  • 55
  • 246
  • 278
3

You can Return to Labels:

fun foo() {
    println("begin foo")
    listOf(1, 2, 3, 4, 5).forEach lit@{
        // local return, meaning this function execution will end
        // the forEach will continue to the next element in the loop
        if (it == 3) return@lit
        
        println("- $it")
    }
    println("done with explicit label")
}

Playground demo here.

acdcjunior
  • 132,397
  • 37
  • 331
  • 304
2

The returns allow us to return from an outer function. The most important use case is returning from a lambda expression

A return statement in an anonymous function will return from the anonymous function itself.

fun foo() {
    ints.forEach(fun(value: Int) {
        if (value == 0) return  // local return to the caller of the anonymous fun, i.e. the forEach loop
        print(value)
    })
}

When returning a value, the parser gives preference to the qualified return, i.e.

return@a 1

means "return 1 at label @a" and not "return a labeled expression (@a 1)".

Return By default returns from the nearest enclosing function or anonymous function.

Break Terminates the nearest enclosing loop.

Continue Proceeds to the next step of the nearest enclosing loop.

More details refer Returns and Jumps,Break and Continue Labels

Ilya
  • 21,871
  • 8
  • 73
  • 92
sasikumar
  • 12,540
  • 3
  • 28
  • 48
1

An alternative to the return might be

mainRepo.fetchUpcomingTrips { trips ->
            if (trips.isEmpty())
                showEmptyViews()
            else {
                //run some code if it's not empty
            }
        }
elect
  • 6,765
  • 10
  • 53
  • 119