What do minusKey and fold do on a CoroutineContext, and when would you use them?
answer
- minusKey removes one element, returns new context
- fold reduces over all elements left-to-right
- Both are non-mutating (immutable context)
- fold underlies plus/toString
- Stripping Job breaks structured concurrency
basics
~10 sminusKey returns a new context with one kind of element removed. fold lets you walk over every element in the context, for example to log or copy them. Both leave the original context unchanged.
solid answer
~40 sminusKey(key: Key<*>) returns a new CoroutineContext identical to the original but without the element registered under that key — used to strip an element (e.g. drop a CoroutineName before passing context elsewhere, or remove the Job when you deliberately don't want to inherit it). fold(initial, operation) reduces over all elements left-to-right, accumulating a result; it's how plus, toString, and copy-like operations are built. A common real use is reconstructing a context without the parent Job: context.minusKey(Job) — though for structured concurrency you usually want a new Job via SupervisorJob() instead. fold is handy for diagnostics: iterate elements to dump the active dispatcher, name, and job state. Both are pure (non-mutating) because the context is immutable.
code
kotlin · 13 linesimport kotlinx.coroutines.*
import kotlin.coroutines.*
fun describe(ctx: CoroutineContext): String =
ctx.fold(StringBuilder()) { sb, e ->
sb.append(e.key).append(' ')
}.toString()
fun main() {
val ctx = Dispatchers.Default + CoroutineName("x") + Job()
println(describe(ctx)) // lists each element's key
println(ctx.minusKey(Job)[Job]) // null — Job removed
}go deeper
Aware minusKey removes an element and the context stays immutable.
Can use minusKey to drop an element and knows fold iterates all elements.
Explains fold as the primitive behind plus/toString and the structured-concurrency danger of stripping Job.
Designs context transformations/diagnostics via fold and reasons about lifecycle implications of removing Job.
## minusKey `fun minusKey(key: Key<*>): CoroutineContext` returns a **new** context with the element for `key` removed (or the same context if it wasn't present). Because contexts are immutable, the original is untouched. ```kotlin import kotlinx.coroutines.* import kotlin.coroutines.* val ctx = Dispatchers.IO + CoroutineName("task") + Job() val noName = ctx.minusKey(CoroutineName) // name dropped, dispatcher + job kept println(noName[CoroutineName]) // null ``` ### When to use minusKey - **Strip the parent Job** when handing a context to code that must not be tied to the current lifecycle (rare; usually you want a new `Job`/`SupervisorJob` instead). - **Sanitize a context** before logging or before reusing it for an unrelated task (e.g. remove a request-scoped element). ## fold `fun <R> fold(initial: R, operation: (R, Element) -> R): R` performs a left-to-right reduction over **every element** in the context. It is the primitive that `plus`, `toString`, and equality are implemented on top of. ```kotlin val names = ctx.fold(mutableListOf<String>()) { acc, element -> acc.apply { add(element::class.simpleName ?: "?") } } // e.g. [CoroutineDispatcher, CoroutineName, JobImpl] (order is implementation-dependent) ``` ### When to use fold - **Diagnostics / tracing**: enumerate active elements to log the dispatcher, name, and job state. - **Context transformation**: build a derived context by folding and selectively re-adding elements (e.g. copy everything except one key — which is exactly how `minusKey` can be expressed). ## Immutability guarantee Neither operation mutates the receiver. `minusKey` and `+`/`fold` all return new context instances, which is why a context can be safely shared across coroutines and inherited by children without defensive copying. ## Caution around Job `minusKey(Job)` removes the lifecycle element. Running a coroutine with no `Job` (or with a detached one) **breaks structured concurrency**: cancellation no longer propagates from a parent. Prefer creating a child scope with `SupervisorJob()` or `CoroutineScope(...)` over manually stripping `Job`.
- Does minusKey modify the original context?No. It returns a new context; the original is unchanged because CoroutineContext is immutable.
- Why is fold considered a primitive operation?Because plus, toString, and copy-style transformations are all implemented in terms of folding over the context's elements.
saying these in an interview costs you the question
- Claiming minusKey mutates the receiver
- Saying fold can only access one element
- Recommending minusKey(Job) as a normal way to detach work (it breaks structured concurrency)
- Confusing fold (over all elements) with get (one element)
- Assuming element iteration order is guaranteed