From 32b5351c216272683bf4b7ad420d108b4a45cb64 Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:54:39 +0200 Subject: [PATCH 1/5] Guard conditions in when expressions This PR adds information about Guard conditions in when expressions and how to use them. --- docs/topics/coding-conventions.md | 20 +++++++- docs/topics/control-flow.md | 76 +++++++++++++++++++++++++++++++ docs/topics/sealed-classes.md | 3 ++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/docs/topics/coding-conventions.md b/docs/topics/coding-conventions.md index 93117481c25..1137f14eb12 100644 --- a/docs/topics/coding-conventions.md +++ b/docs/topics/coding-conventions.md @@ -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) { @@ -991,6 +991,24 @@ when (x) { Prefer using `when` if there are three or more options. +### Guard conditions in when expression + +Prefer using 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 is Status.Error) -> "no information" +} +``` + +Instead of: + +```kotlin +when (status) { + is Status.Ok if status.info.isEmpty() || status is Status.Error -> "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. diff --git a/docs/topics/control-flow.md b/docs/topics/control-flow.md index 6b3e692d319..f5d034067ec 100644 --- a/docs/topics/control-flow.md +++ b/docs/topics/control-flow.md @@ -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 + +> 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). +> +{type="warning"} + +Starting from Kotlin 2.1, you can use guard conditions in `when` expressions or statements with a subject. + +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. + +To include a guard condition in a branch, place the guard condition together with the `if` keyword after the primary condition: + +```kotlin +sealed interface Animal { + class Cat(val mouseHunter: Boolean) : Animal + data object Dog : Animal +} + +fun feedAnimal(animal: Animal) { + when (animal) { + // Branch with only primary condition. Returns `feedDog()` when `Animal` is `Dog` + 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 both branches with and without a guard condition. +The code in the branches with a guard condition runs only if 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, if 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). +It is [strongly recommended](coding-conventions.md#guard-conditions-in-when-expression) to use parentheses around the boolean expressions to avoid confusion: + +```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, run the following command: +> +> `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#. diff --git a/docs/topics/sealed-classes.md b/docs/topics/sealed-classes.md index 85dff5c3626..9dcf145b7ff 100644 --- a/docs/topics/sealed-classes.md +++ b/docs/topics/sealed-classes.md @@ -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 utilize 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 From 3145a59700d5fde00134827e187dd9c694bdb23b Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:54:46 +0200 Subject: [PATCH 2/5] Alejandro's review --- docs/topics/coding-conventions.md | 4 ++-- docs/topics/control-flow.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/topics/coding-conventions.md b/docs/topics/coding-conventions.md index 1137f14eb12..0d39eb6cc13 100644 --- a/docs/topics/coding-conventions.md +++ b/docs/topics/coding-conventions.md @@ -997,7 +997,7 @@ Prefer using parentheses when combining multiple boolean expressions in `when` e ```kotlin when (status) { - is Status.Ok if (status.info.isEmpty() || status is Status.Error) -> "no information" + is Status.Ok if (status.info.isEmpty() || status.info.id == null) -> "no information" } ``` @@ -1005,7 +1005,7 @@ Instead of: ```kotlin when (status) { - is Status.Ok if status.info.isEmpty() || status is Status.Error -> "no information" + is Status.Ok if status.info.isEmpty() || status.info.id == null -> "no information" } ``` diff --git a/docs/topics/control-flow.md b/docs/topics/control-flow.md index f5d034067ec..20598f509cc 100644 --- a/docs/topics/control-flow.md +++ b/docs/topics/control-flow.md @@ -188,12 +188,12 @@ Starting from Kotlin 2.1, you can use guard conditions in `when` expressions or 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. -To include a guard condition in a branch, place the guard condition together with the `if` keyword after the primary condition: +To include a guard condition in a branch, place the guard condition after the primary condition, separated by `if`: ```kotlin sealed interface Animal { class Cat(val mouseHunter: Boolean) : Animal - data object Dog : Animal + data class Dog(val breed: String) : Animal } fun feedAnimal(animal: Animal) { @@ -208,8 +208,8 @@ fun feedAnimal(animal: Animal) { } ``` -In a single `when` expression, you can combine both branches with and without a guard condition. -The code in the branches with a guard condition runs only if the primary condition and the guard condition evaluate to true. +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, if none of the conditions matches, none of the branches is executed. From 7240fff3767610ac0ab8da8b359fb802175ebf2a Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:02:48 +0200 Subject: [PATCH 3/5] chore: adding `is` in a branch Co-authored-by: Alejandro Serrano --- docs/topics/control-flow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/control-flow.md b/docs/topics/control-flow.md index 20598f509cc..6050aab1993 100644 --- a/docs/topics/control-flow.md +++ b/docs/topics/control-flow.md @@ -199,7 +199,7 @@ sealed interface Animal { fun feedAnimal(animal: Animal) { when (animal) { // Branch with only primary condition. Returns `feedDog()` when `Animal` is `Dog` - Animal.Dog -> feedDog() + 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 From f570eee5ce7e64b29cb95e4446a1b4d83ac1818c Mon Sep 17 00:00:00 2001 From: alejandrapedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:03:09 +0200 Subject: [PATCH 4/5] chore: adding `data` in code line Co-authored-by: Alejandro Serrano --- docs/topics/control-flow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/control-flow.md b/docs/topics/control-flow.md index 6050aab1993..a18d9b3481c 100644 --- a/docs/topics/control-flow.md +++ b/docs/topics/control-flow.md @@ -192,7 +192,7 @@ To include a guard condition in a branch, place the guard condition after the pr ```kotlin sealed interface Animal { - class Cat(val mouseHunter: Boolean) : Animal + data class Cat(val mouseHunter: Boolean) : Animal data class Dog(val breed: String) : Animal } From 33c194c103d35ce8eb3b41852d04a5a11f9eec5c Mon Sep 17 00:00:00 2001 From: alepedroza <39709865+AlejandraPedroza@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:19:28 +0100 Subject: [PATCH 5/5] Andrey review --- docs/topics/coding-conventions.md | 2 +- docs/topics/control-flow.md | 22 +++++++++++----------- docs/topics/sealed-classes.md | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/topics/coding-conventions.md b/docs/topics/coding-conventions.md index 0d39eb6cc13..c54fb4720aa 100644 --- a/docs/topics/coding-conventions.md +++ b/docs/topics/coding-conventions.md @@ -993,7 +993,7 @@ Prefer using `when` if there are three or more options. ### Guard conditions in when expression -Prefer using parentheses when combining multiple boolean expressions in `when` expressions or statements with [guard conditions](control-flow.md#guard-conditions-in-when-expressions): +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) { diff --git a/docs/topics/control-flow.md b/docs/topics/control-flow.md index a18d9b3481c..1e5a9d19d89 100644 --- a/docs/topics/control-flow.md +++ b/docs/topics/control-flow.md @@ -182,13 +182,13 @@ The scope of variable introduced in *when* subject is restricted to the body of > 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). > -{type="warning"} +{style="warning"} -Starting from Kotlin 2.1, you can use guard conditions in `when` expressions or statements with a subject. +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. -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. - -To include a guard condition in a branch, place the guard condition after the primary condition, separated by `if`: +To include a guard condition in a branch, place it after the primary condition, separated by `if`: ```kotlin sealed interface Animal { @@ -212,9 +212,9 @@ In a single `when` expression, you can combine branches with and without guard c 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, if none of the conditions matches, none of the branches is executed. +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). +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`: @@ -232,7 +232,7 @@ when (animal) { ``` Combine multiple guard conditions within a single branch using the boolean operators `&&` (AND) or `||` (OR). -It is [strongly recommended](coding-conventions.md#guard-conditions-in-when-expression) to use parentheses around the boolean expressions to avoid confusion: +Use parentheses around the boolean expressions to [avoid confusion](coding-conventions.md#guard-conditions-in-when-expression): ```kotlin when (animal) { @@ -240,14 +240,14 @@ when (animal) { } ``` -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")`). +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, run the following command: +> To enable guard conditions in Gradle, add the following line to the `build.gradle.kts` file: > > `kotlin.compilerOptions.freeCompilerArgs.add("-Xwhen-guards")t` > diff --git a/docs/topics/sealed-classes.md b/docs/topics/sealed-classes.md index 9dcf145b7ff..134344cba60 100644 --- a/docs/topics/sealed-classes.md +++ b/docs/topics/sealed-classes.md @@ -200,7 +200,7 @@ fun main() { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.5"} -When using sealed classes with `when` expressions, you can also utilize guard conditions to include additional checks in a single branch. +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