In Rego, what does the wildcard _ do in a reference like input.spec.containers[_].name?
answer
- there is no for loop here
- an unnamed variable still binds
- does it mean any, or all?
- the body runs once per match
- two underscores are two variables
basics
~20 sThe underscore is an anonymous variable, so the reference iterates: OPA tries every element of the array and the expression succeeds if at least one element makes it hold. Each underscore is a separate variable whose value you cannot refer to later.
solid answer
~50 sAn unbound variable inside a reference turns that reference into iteration, and `_` is simply an unbound variable you did not bother to name. `input.spec.containers[_].name` means "for some index, the name at that index", so the expression is defined when at least one container exists — it is existential, not universal. The rest of the rule body is evaluated once per binding that satisfies it, which is why a partial set rule like `deny contains msg` can emit several messages from one body. Two things bite people: every occurrence of `_` is a **distinct** variable, so `containers[_].name == containers[_].image` compares across unrelated pairs of elements; and because `_` has no name you cannot use the matched element elsewhere in the body. When you need the value, name it — `some i` plus `containers[i]`, or `some c in containers`.
go deeper
Know that the underscore is an anonymous variable and that putting one in a path makes the reference walk the collection. Remember it means at least one element matched, not all of them.
Explain the mechanics: the body is evaluated once per satisfying binding, each underscore is an independent variable, and that is why a partial set rule accumulates one entry per offender with no loop.
Demonstrate the failure judgment — spot a rule that reads as universal but is existential, and explain when to name the binding so a message or a cross-reference points at the element that actually matched.
Own the reviewability angle: decide whether your policy repo prefers value iteration over index wildcards precisely because the wildcard form hides which element a denial refers to.
Rego has no `for` loop. Iteration is a side effect of how the evaluator resolves references: when a reference contains a variable that is not yet bound, the evaluator searches for every binding that makes the reference defined, and evaluates the surrounding body once for each of them. ## The wildcard is just an unnamed variable These two bodies mean the same thing: - `input.spec.template.spec.containers[_].resources.limits` - `some i; input.spec.template.spec.containers[i].resources.limits` Both ask: is there **some** index into `containers` for which the path `resources.limits` exists? The difference is that in the second form the index has a name, so you can use it — to index a parallel structure, or to build a message. Using `_` is a deliberate signal to the reader that you do not care which element matched. ## Existential, not universal This is the single most common misreading. A body containing `containers[_].resources.limits` succeeds when **at least one** container has limits. It does not assert that all of them do. Written as a deny rule, that means a chart with five containers where only one sets limits will not be flagged, which is precisely the class of bug that lets an unlimited workload reach a cluster. Universal quantification needs `every`, or a negation over a helper that finds a counterexample. ## One binding, one evaluation Because the body runs once per satisfying binding, the shape of the rule decides what you get back: - A **partial set** rule (`deny contains msg if { ... }`) collects one entry per binding, deduplicated. This is why a well-written deny rule reports every offender without any explicit looping. - A **complete** rule (`msg := ... if { ... }`) is supposed to have one value. If different bindings produce different values, evaluation fails with a conflict error rather than picking one; if they all produce the same value, it is fine. So "why did my rule return three messages" and "why did my rule blow up with a conflict" have the same root cause: an unbound variable in the body. ## Each `_` is its own variable `input.spec.template.spec.containers[_].name == input.spec.template.spec.containers[_].image` does **not** compare a container's name with its own image. The two wildcards are independent, so the expression succeeds if any container's name equals any container's image. When two references must line up, bind one index and reuse it: - `some i; c := containers[i]` then use `c.name` and `c.image`. - Or better, `some c in containers` and use `c` twice — the value form removes the chance of index confusion entirely. ## `some`, `in`, and scope `some i` declares `i` as local to the rule body. Declaring it explicitly is worth doing even when the rule would work without it: it makes the scope obvious and avoids a name in the body silently referring to something already defined in the package or under `data`. The value form, `some c in input.spec.template.spec.containers`, iterates the elements directly, and `some k, v in obj` gives you key and value together. The membership form without `some` — `"cpu" in object.keys(limits)` — is a plain boolean test rather than an iteration. ## Iterating over keys you choose A neat consequence of "an unbound variable iterates" is that the variable can sit in the middle of a path, not only at an array index. Binding a field name from a list of names lets one body cover several sibling collections — regular containers, init containers and ephemeral containers all live side by side under the same pod spec, and one rule can walk all three by iterating the field name. A binding whose reference does not exist simply contributes no result, so a workload without init containers is skipped rather than erroring. ## What to say in an interview Summarise it as three claims: an unbound variable in a reference means iteration; iteration is existential unless you wrap it in `every` or a negation; and the number of results a rule returns is the number of satisfying bindings, which is why partial set rules and iteration are designed for each other.
- When should you write some i instead of the underscore?Whenever you need the matched element or its index later in the body — to build a message, to index a parallel collection, or to keep two references pointing at the same element. `_` says "I do not care which"; if the rest of the body cares, name it. The value form `some c in coll` is usually clearer still.
- How many results does a partial set rule return when its body iterates a five-element array?One entry per binding that satisfies the whole body, deduplicated by value — so between zero and five. The set collects them automatically; there is no accumulator to write. A complete rule under the same body would raise a conflict error if the bindings produced different values.
- Does containers[_].resources.limits assert that every container sets limits?No. It is existential: it is defined as soon as one container has that path, and it says nothing about the others. Use `every c in containers { c.resources.limits }`, or find a counterexample and negate it. Reading iteration as universal is how a partly-unlimited workload slips through.
saying these in an interview costs you the question
- Reads containers[_] as a check on all elements
- Thinks two underscores in one expression share a value
- Expects to reference the matched element after using _
- Says Rego needs an explicit loop to collect violations
- Assumes a complete rule silently picks one of several bindings