Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guard conditions in when expressions #4417

Open
wants to merge 5 commits into
base: 2-1-0-doc-update
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion docs/topics/coding-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ For example, use this syntax with `if`:
if (x == null) ... else ...
```

instead of this one with `when`:
Instead of this one with `when`:

```kotlin
when (x) {
Expand All @@ -991,6 +991,24 @@ when (x) {

Prefer using `when` if there are three or more options.

### Guard conditions in when expression

Use parentheses when combining multiple boolean expressions in `when` expressions or statements with [guard conditions](control-flow.md#guard-conditions-in-when-expressions):

```kotlin
when (status) {
is Status.Ok if (status.info.isEmpty() || status.info.id == null) -> "no information"
}
```

Instead of:

```kotlin
when (status) {
is Status.Ok if status.info.isEmpty() || status.info.id == null -> "no information"
}
```

### Nullable Boolean values in conditions

If you need to use a nullable `Boolean` in a conditional statement, use `if (value == true)` or `if (value == false)` checks.
Expand Down
76 changes: 76 additions & 0 deletions docs/topics/control-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,82 @@ fun Request.getBody() =

The scope of variable introduced in *when* subject is restricted to the body of this *when*.

### Guard conditions in when expressions
AlejandraPedroza marked this conversation as resolved.
Show resolved Hide resolved

> Guard conditions are an [experimental feature](components-stability.md#stability-levels-explained) that may be changed at any time.
> We would appreciate your feedback in [YouTrack](https://youtrack.jetbrains.com/issue/KT-71140/Guard-conditions-in-when-expressions-feedback).
>
{style="warning"}

Guard conditions allow you to include
more than one condition to the branches of a `when` expression, making complex control flow more explicit and concise.
You can use guard conditions in `when` expressions or statements with a subject.

To include a guard condition in a branch, place it after the primary condition, separated by `if`:

```kotlin
sealed interface Animal {
data class Cat(val mouseHunter: Boolean) : Animal
data class Dog(val breed: String) : Animal
}

fun feedAnimal(animal: Animal) {
when (animal) {
// Branch with only primary condition. Returns `feedDog()` when `Animal` is `Dog`
is Animal.Dog -> feedDog()
// Branch with both primary and guard conditions. Returns `feedCat()` when `Animal` is `Cat` and is not `mouseHunter`
is Animal.Cat if !animal.mouseHunter -> feedCat()
// Returns "Unknown animal" if none of the above conditions match
else -> println("Unknown animal")
}
}
```

In a single `when` expression, you can combine branches with and without guard conditions.
The code in a branch with a guard condition runs only if both the primary condition and the guard condition evaluate to true.
If the primary condition does not match, the guard condition is not evaluated.

If you use guard conditions in `when` statements without an `else` branch, and none of the conditions matches, none of the branches is executed.

Otherwise, if you use guard conditions in `when` expressions without an `else` branch, the compiler requires you to declare all the possible cases to avoid runtime errors.

Additionally, guard conditions support `else if`:

```kotlin
when (animal) {
// Checks if `animal` is `Dog`
is Animal.Dog -> feedDog()
// Guard condition that checks if `animal` is `Cat` and not `mouseHunter`
is Animal.Cat if !animal.mouseHunter -> feedCat()
// Returns giveLettuce() if none of the above conditions match and animal.eatsPlants is true
else if animal.eatsPlants -> giveLettuce()
// Returns "Unknown animal" if none of the above conditions match
else -> println("Unknown animal")
}
```

Combine multiple guard conditions within a single branch using the boolean operators `&&` (AND) or `||` (OR).
Use parentheses around the boolean expressions to [avoid confusion](coding-conventions.md#guard-conditions-in-when-expression):

```kotlin
when (animal) {
is Animal.Cat if (!animal.mouseHunter && animal.hungry) -> feedCat()
}
```

You can use guard conditions in any `when` expression or statement with a subject, except the case when you have multiple conditions separated by a comma.
For example, `0, 1 -> print("x == 0 or x == 1")`.

> To enable guard conditions in CLI, run the following command:
>
> `kotlinc -Xwhen-guards main.kt`
>
> To enable guard conditions in Gradle, add the following line to the `build.gradle.kts` file:
>
> `kotlin.compilerOptions.freeCompilerArgs.add("-Xwhen-guards")t`
>
{type="note"}

## For loops

The `for` loop iterates through anything that provides an iterator. This is equivalent to the `foreach` loop in languages like C#.
Expand Down
3 changes: 3 additions & 0 deletions docs/topics/sealed-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ fun main() {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.5"}

When using sealed classes with `when` expressions, you can also add guard conditions to include additional checks in a single branch.
For more information, see [Guard conditions in when expressions](control-flow.md#guard-conditions-in-when-expressions).

> In multiplatform projects, if you have a sealed class with a `when` expression as an
> [expected declaration](multiplatform-expect-actual.md) in your common code, you still need an `else` branch.
> This is because subclasses of `actual` platform implementations may extend sealed classes that
Expand Down