Why this fires¶
The _ placeholder stands in for "the value being piped" or "the argument this short lambda receives". It only has a defined meaning in two positions:
- As a direct argument in the RHS call of a pipe stage (
|>/?>):x |> clamp(0, _, 100)splices the piped value into that slot and the pipe disappears — it becomesclamp(0, x, 100). - In a proved unary callback argument:
xs.map(_ * 2),xs.map(_)andusers.map(format_name(_.name))become one-parameter lambdas. Every_in the expression denotes the same parameter; nested callback slots bind independently. Existing access-rooted shorthand also remains available for untyped callback calls.
Everywhere else a bare _ is not a value — there's nothing for it to stand for — so the compiler rejects it as TE132. Three shapes trigger it:
1. Two or more _ in the same pipe stage¶
Only one _ per stage can be resolved to "the piped value" — a second one has nothing left to bind to.
fn f(a: int, b: int) -> int { return a }
let x = 5
let y = x |> f(_, _)
// ^^^^^^^ error[TE132]: at most one `_` per pipe stage2. A stray _ outside a pipe/short-lambda position¶
let x = 1 + _
// ^ error[TE132]: `_` is only valid in the RHS of `|>`/`?>` or in a unary callback argument
fn f(a: int) -> int { return a }
let z = f(_)
// ^ error[TE132]: `_` is only valid in the RHS of `|>`/`?>` or in a unary callback argumentA bare _ or scalar expression requires an actual unary callable parameter.
Thus f(_) is invalid above because f expects an integer, while
xs.map(_ + 1) is valid. An unresolved dynamic call does not establish the
callback contract; name the parameter in an explicit lambda instead.
Inside a unary callback, ordinary nested value calls use its parameter:
users.filter(check(_.age)) passes an age to check. A nested callback slot
binds its own _. Explicit lambdas, blocks, collections, control flow and pipes
also delimit scopes.
3. _ as the left-hand side of a pipe¶
fn f(a: int) -> int { return a }
let y = _ |> f(_)
// ^ error[TE132]: `_` cannot be the left-hand side of a pipe_ is only ever an RHS-argument placeholder or a short-lambda chain head — it can never be the value flowing into a pipe.
Fix it¶
1. Use at most one _ per stage¶
fn f(a: int, b: int) -> int { return a }
let x = 5
let y = x |> f(_, 2) // ok — one placeholderIf the piped value is needed in more than one argument position, name it first:
let tmp = x
let y = f(tmp, tmp)2. Use _ in a callback position or a pipe stage¶
let ys = xs.map(_.name) // ok — `_` is the chain head, `.name` is the access
let adults = xs.filter(_.active && _.age >= 18) // one shared parameter
let doubled = [1, 2].map(_ * 2) // a proved unary callback
let y = x |> f(_, 100) // ok — `_` fills the RHS call's placeholder slotA bare _ outside those positions has no defined meaning; bind it to a name instead:
fn f(a: int) -> int { return a }
let x = 5
let z = f(x) // fixed3. Don't pipe from a bare _¶
fn f(a: int) -> int { return a }
let x = 5
let y = x |> f(_) // ok — `_` is the RHS's placeholder, not the LHSSee also¶
- /docs/operators — pipe (
|>), try-pipe (?>), and placeholder syntax. TE131— named-argument call validation, the other TE13x front-end desugar.