Not nullable value required to call 'component1()' function of destructuring declaration initializer
Asked Answered
L

3

7

Is it possible to make the following code to compile in Kotlin?

val variable: String? = "string"

val (a, b) = variable?.run {
    1 to 2
}  
Landseer answered 4/3, 2020 at 19:27 Comment(0)
J
15

The compiler does not allow destructuring because the expression on the right-hand side is typed as a nullable Pair<Int, Int>?, and it's unclear what values a and b should get in case variable is null.

To solve this, you need to get a not-null expression after =.

There's a lot of different ways to deal with nullable values and produce a not-null value from a nullable one, see: In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them

For example, if you want to provide fallback values for a and b, then use the ?: operator as follows:

val (a, b) = variable?.run {
    1 to 2
} ?: (0 to 0)

An alternative, for example, would be to check variable for null first:

val (a, b) = checkNotNull(variable) { "variable should never be null" }.run {
    1 to 2
}
Jeunesse answered 4/3, 2020 at 19:37 Comment(4)
I think a and b should be both null if variable is null or I'm missing something? variable is allowed to be null and the fallback values should be null in that case.Landseer
I added ?: (null to null) at the end and it worked, but I believe it could be done automatically.Landseer
You actually need null as Int? to null as Int? if you want a and b to be Int? rather than Any?.Jointed
?: (null to null) does not work for me in latest kotlin, same exception.Criminate
J
6

Null doesn't have any destructuring declarations. If you want a value of null to destructure like it's a pair of nulls, you could add these extensions:

operator fun <T> Pair<T, *>?.component1() = this?.component1()
operator fun <T> Pair<*, T>?.component2() = this?.component2()

Otherwise, as the other answer shows, you need to provide a default using the Elvis operator.

It's not automatic because it doesn't know what you want. Depending on what you're doing with it, 0 to 0 may be most appropriate, or maybe -1 to -1 or 0 to null or null to null.

Jointed answered 4/3, 2020 at 20:23 Comment(2)
There is the elvis operator if we would like to define a default value other than null. I think it would be reasonable to return null to null.Landseer
You could open an issue on YouTrack to suggest it. It's not a part of the stdlib. I suppose if there were a default, it would have to be null to null so it would work for any types.Jointed
C
1

The question is, what do you want to do if your variable is null? If you want to throw an exception, go with require or check as @hotkey suggested.

However I have the case where I just want to return if the value is null. So I wrote myself a little helper function that allows for this:

private inline fun <T> T?.exitIfNull(exitBlock: () -> Nothing): T {
    if (this == null)
        exitBlock()
    else
        return this
}

You can call this function as follows:

val (a, b) = variable?.run {
    1 to 2
}.exitIfNull {
    return
}

A nice little use of the Nothing keyword in Kotlin that I personally find quite fascinating

Corncob answered 6/3, 2021 at 22:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.