Why does checking if a nullable element is in a no...
# stdlib
e
Why does checking if a nullable element is in a non nullable container not contract that the element be not null if true?
d
Code example pls
e
Copy code
val array = arrayOf("first", "second", "third");
fun String.example() { println(this) }
fun foo(bar: String?) {
    if (bar in array)
        bar.example()
}
In this example because bar is in the array... we know that bar is not null since the array is of nonnullable strings.
e
it can't in general,
Copy code
val array = arrayOfNulls<String>(3) as Array<String>
val bar: String? = null
check(bar in array)
will succeed at runtime
j
I think
arrayOfNulls<String>(3) as Array<String>
might confound lots of other things that people would generally expect to work too 😂
The code in stdlib 1.6.0 for
contains
is
Copy code
public operator fun <@kotlin.internal.OnlyInputTypes T> Array<out T>.contains(element: T): Boolean {
    return indexOf(element) >= 0
}
Giving it a quick go, it seems possible to define a function that does allow a smart cast:
Copy code
fun <T : Any> Array<T>.smartContains(element: T?): Boolean {
    contract {
        returns(true) implies (element != null)
    }
    return indexOf(element) >= 0
}

val array = arrayOf("first", "second", "third");
fun String.example() { println(this) }
fun foo(bar: String?) {
    if (array.smartContains(bar))
        bar.example()
}
That said, Kotlin doesn't appear to support contracts inside operator functions at the moment, so this function couldn't be included alongside the original one anyway. And I have no idea if there would be other consequences of including it, e.g. overload ambiguity (possibly not because of the nullability differences, but 🤷)
e
Humm, I wonder if contracts will ever eventually make their way to operator functions. But the issue of having 2 specializations of the same function is certainly potentially problematic I guess.