|
9 | 9 | package kotlinx.coroutines.flow
|
10 | 10 |
|
11 | 11 | import kotlinx.coroutines.flow.internal.*
|
| 12 | +import kotlinx.coroutines.internal.Symbol |
12 | 13 | import kotlin.jvm.*
|
13 | 14 |
|
14 | 15 | /**
|
@@ -47,33 +48,43 @@ public suspend inline fun <T, R> Flow<T>.fold(
|
47 | 48 | }
|
48 | 49 |
|
49 | 50 | /**
|
50 |
| - * The terminal operator, that awaits for one and only one value to be published. |
| 51 | + * The terminal operator that awaits for one and only one value to be emitted. |
51 | 52 | * Throws [NoSuchElementException] for empty flow and [IllegalStateException] for flow
|
52 | 53 | * that contains more than one element.
|
53 | 54 | */
|
54 | 55 | public suspend fun <T> Flow<T>.single(): T {
|
55 | 56 | var result: Any? = NULL
|
56 | 57 | collect { value ->
|
57 |
| - if (result !== NULL) error("Expected only one element") |
| 58 | + require(result == NULL) { "Flow has more than one element" } |
58 | 59 | result = value
|
59 | 60 | }
|
60 | 61 |
|
61 |
| - if (result === NULL) throw NoSuchElementException("Expected at least one element") |
62 |
| - @Suppress("UNCHECKED_CAST") |
| 62 | + if (result === NULL) throw NoSuchElementException("Flow is empty") |
63 | 63 | return result as T
|
64 | 64 | }
|
65 | 65 |
|
66 | 66 | /**
|
67 |
| - * The terminal operator, that awaits for one and only one value to be published. |
68 |
| - * Throws [IllegalStateException] for flow that contains more than one element. |
| 67 | + * The terminal operator that awaits for one and only one value to be emitted. |
| 68 | + * Returns the single value or `null`, if the flow was empty or emitted more than one value. |
69 | 69 | */
|
70 | 70 | public suspend fun <T> Flow<T>.singleOrNull(): T? {
|
71 |
| - var result: T? = null |
| 71 | + var result: Any? = NULL |
72 | 72 | collect { value ->
|
73 |
| - if (result != null) error("Expected only one element") |
74 |
| - result = value |
| 73 | + /* |
| 74 | + * result === NULL -> first value |
| 75 | + * result === user value -> we already had first value and second one arrived |
| 76 | + * result === DONE -> we've seen more than one value, time to return it |
| 77 | + * as well. |
| 78 | + */ |
| 79 | + result = if (result === NULL) { |
| 80 | + value |
| 81 | + } else { |
| 82 | + // Indicator that more than one value was observed |
| 83 | + DONE |
| 84 | + } |
75 | 85 | }
|
76 |
| - return result |
| 86 | + // Symbols are never leaked, so it's one comparison versus two |
| 87 | + return if (result is Symbol) null else result as T |
77 | 88 | }
|
78 | 89 |
|
79 | 90 | /**
|
|
0 commit comments