46

I want to write guard let statement in Kotlin like Swift.

For example:

guard let e = email.text , !e.isEmpty else { return }

Any advice or sample code?

Zoe
  • 27,060
  • 21
  • 118
  • 148
tsniso
  • 621
  • 2
  • 7
  • 16
  • Is "guard let" like "if let"? https://stackoverflow.com/questions/46723729/swift-if-let-statement-in-kotlin – Carcigenicate Apr 13 '19 at 20:44
  • yes, kind of :) – tsniso Apr 13 '19 at 20:48
  • Then check that link. It may have an answer. – Carcigenicate Apr 13 '19 at 20:49
  • Please read [How do I avoid misusing tags?](https://meta.stackoverflow.com/questions/354427/how-do-i-avoid-misusing-tags), and [the tagging guide](/help/tagging). You used several tags that had different meanings (i.e. [tag:let] and [tag:guard]), and some that had nothing to do with the question (swift; don't tag source languages - the question is about Kotlin, not Swift) – Zoe Apr 13 '19 at 20:54

5 Answers5

78

Try

val e = email.text?.let { it } ?: return

Explanation: This checks if the property email.text is not null. If it is not null, it assigns the value and moves to execute next statement. Else it executes the return statement and breaks from the method.

Edit: As suggested by @dyukha in the comment, you can remove the redundant let.

val e = email.text ?: return

If you want to check any other condition, you can use Kotlin's if expression.

val e = if (email.text.isEmpty()) return else email.text

Or try (as suggested by @Slaw).

val e = email.text.takeIf { it.isNotEmpty() } ?: return

You may also like to try guard function as implemented here: https://github.com/idrougge/KotlinGuard

farhanjk
  • 1,652
  • 16
  • 17
  • 11
    Why not just `email.text ?: return`? –  Apr 13 '19 at 21:14
  • Agreed @dyukha. Even sweeter! – farhanjk Apr 13 '19 at 21:15
  • But the condition in the question is different, isn't it? I don't quite understand what `guard let` does, but it seems that you should check that `email.text` is empty, not compare with null. –  Apr 13 '19 at 21:17
  • The `guard` swift statement is actually a glorified `if` statement. guard let name = nameField.text else { show("No name to submit") return } – farhanjk Apr 13 '19 at 21:18
  • I mean, in the question the condition is `!e.isEmpty`, not that `e` is not null. –  Apr 13 '19 at 21:20
  • 1
    Updated my answer. – farhanjk Apr 13 '19 at 21:21
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191799/discussion-between-farhanjk-and-dyukha). – farhanjk Apr 13 '19 at 21:22
  • 2
    If you still want to use the elvis operator, you can use: `val e = email.text.takeIf { it.isNotEmpty() } ?: return`. – Slaw Apr 13 '19 at 21:28
  • I want to do something before return so what should I do ? – tsniso Apr 13 '19 at 21:35
  • 1
    val e = email.text.takeIf { it.isNotEmpty() } ?: print("this is not cool!").also { return } – farhanjk Apr 13 '19 at 21:38
28

Try

val e = email.text ?: run {
    // do something, for example: Logging
    return@outerFunction
}

if you want to do something else before return.

Louis
  • 364
  • 3
  • 11
KYHSGeekCode
  • 1,068
  • 2
  • 12
  • 30
3

I have a slightly different solution, if you are looking to recreate the ability that swift has of unwrapping multiple optionals and then using the unwrapped variables.

consider adding these lines in a Kotlin file

    
    inline fun <T1, T2, T3, R> guard(
        p1: T1?, p2: T2?, p3: T3?,
        condition: Boolean = true,
        block: (T1, T2, T3) -> R
    ): R? = if (p1 != null && p2 != null && p3 != null && condition)
        block(p1, p2, p3)
    else null
    
    inline fun <T1, T2, T3, T4, R> guard(
        p1: T1?, p2: T2?, p3: T3?, p4: T4?,
        condition: Boolean = true,
        block: (T1, T2, T3, T4) -> R
    ): R? = if (p1 != null && p2 != null && p3 != null && p4 != null && condition)
        block(p1, p2, p3, p4)
    else null

(I did have up to p9 but saved it for brevity)

this means you do now do

    //given you have 

    var firstName: String? = null
    var lastName: String? = null
    var email: String? = null
    var password: String? = null
            
    fun createUser(name: String, lname: String, mail: String, pword: String) {
        // some work            
    }

you can now use it like this

    guard(firstName, lastName, email, password){ fName, lName, mail, pword ->            
        createUser(fName, lName, mail, pword) // all your variables are unwrapped!
    } ?: return // <- here if you want an early return

    // or
    guard(firstName, lastName, email, password,
        condition = email.isValid 
    ) { fName, lName, mail, pword -> 
        // N.B this will not execute if the email is not valid
        createUser(fName, lName, mail, pword)
    }

As this function is inlined you can use it in with coroutines and you can return a value from the block and use it.

Edit: I have put all the code in a gist here https://gist.github.com/markGilchrist/b699b00e9baeaa5e725a2eb1e9e7f5d3

Mark Gilchrist
  • 1,972
  • 3
  • 24
  • 44
3

I used this:

it ?: return

Simple and short

Mahdi Moqadasi
  • 2,029
  • 4
  • 26
  • 52
2

I found another way to do that. Simply create following function:

inline fun <T: Any> T?.guard(block: () -> Unit): T {
    if (this == null) block(); return this!!
}

Then you can use it like that:

val date: Date?
date = Date()
val nonNullableDate = date.guard { return }

nonNullableDate is then of type Date.

Anyway, as it is not during creation of a variable like in swift, NullPointerExceptions are possible so make sure you exit the code part with a return i.e.

Hopefully Kotlin is adding a guard keyword in the future.

kaulex
  • 2,921
  • 3
  • 11
  • 38