Destructuring¶
Accepted
Accepted for V1 binding-pattern destructuring; nested patterns, rest binding, defaults, destructuring assignment, and field move-out are deferred or rejected as documented here.
Destructuring is binding syntax, not pattern matching, overload resolution, or hidden effect inference. The same binding patterns should work everywhere ordinary bindings are introduced, including function parameters and conditional optional bindings.
Struct Destructuring¶
Struct destructuring binds fields by name. Shorthand { field } binds local field from source field field.
Rename uses local = field, matching ordinary binding direction: const local = source.field. Catalyst uses = for renaming so : remains visually reserved for type annotation.
A struct entry may borrow the source field with &field or local = &field. To explicitly ignore a named field, write _ = field; bare _ is not a valid struct entry because it names no source field. _ = field is semantically unnecessary when unmentioned fields are already ignored, but it can document intent or satisfy lint policy.
Partial struct destructuring is allowed: unmentioned fields are ignored, while mentioned fields that do not exist are errors.
Struct destructuring defaults are rejected; fields either exist in the source type or the program is invalid. Rest binding and nested destructuring are deferred. Rest destructuring such as { alloc, ...rest } is deferred because it implies constructing a residual value or view and raises ownership questions.
Array Destructuring¶
Array destructuring binds positions by order and requires exact arity in V1. Use _ to explicitly ignore an array position.
Borrow entries are struct-only in V1. Use explicit indexing such as const x = &pair[0] to borrow array elements.
Ignore Bindings¶
_ is a general ignore binding pattern anywhere bindings are introduced. It does not introduce a binding, may appear multiple times in the same pattern or scope, and suppresses unused-binding lints by construction.
Ignoring a resource value with _ is lintable as ownership/discarded-resource.
Identifiers such as _name are normal bindings semantically. Treating them as intentionally unused is lint policy, not compiler semantics.
Value Binding¶
Destructuring is value binding. Destructuring a field or array element from an lvalue follows the same copy eligibility rules as ordinary field or index value access. It does not secretly borrow or move out:
const { file } = owner // error if owner.file is non-copy
const file = &owner.file // explicit borrow
Struct destructuring works over struct values and pointer-to-struct sources. Pointer sources follow ordinary field-access-through-pointer rules, exactly the same as source.field; destructuring does not define a separate auto-deref rule or dereference-depth rule:
const { alloc, diag } = ctx // ctx: *CompilerContext
This binds as if accessing ctx.alloc and ctx.diag.
If ordinary field access would reject ctx.alloc, the destructuring form is rejected too. If ordinary field access later allows a different pointer dereference depth, destructuring inherits that behavior automatically.
Borrow Entries¶
Borrow field entries are allowed in struct destructuring when the source is an addressable place:
const { &file } = owner
const { handle = &file } = owner
These bind pointers to the source field rather than copying or moving the field value. They desugar like ordinary address-of field access:
const file = &owner.file
const handle = &owner.file
Pointer-source destructuring composes with borrow entries by using the same field access first and then ordinary address-of behavior. Pointer mutability follows the normal &source.field rule. Borrow entries are binding syntax only; they do not change ownership of the source aggregate.
For example:
var owner: Owner = ...
const { &file } = owner
// file: *File
const owner2: Owner = ...
const { &file } = owner2
// file: *const File
The const on the local binding means the pointer binding is not reassignable; it does not force the pointee const.
var on a destructuring declaration makes the produced local bindings reassignable, including pointer bindings:
var { &file } = owner
// file: *File, and the local pointer binding may be reassigned
Binding mutability applies to the whole pattern. Per-binding const or var inside destructuring patterns is invalid in V1:
const { var a, b } = value // error
var { const a, b } = value // error
V1 Boundaries¶
Destructuring is binding syntax only in V1. It introduces bindings through const, var, parameters, loop bindings, or conditional bindings. Destructuring assignment to existing bindings is deferred:
[x, y] = pair
{ x = left, y = right } = point
Destructuring is not valid in comptime generic binders or impl binders in V1. Those binders introduce type/comptime names and remain explicit.
Destructuring does not provide field move-out in V1. Even for by-value aggregate parameters, destructuring a non-copy field by value is rejected; keep the parameter whole, use explicit APIs, or borrow the field:
fn consume({ file }: Owner) void {
} // error if file is non-copy
fn inspect({ &file }: Owner) void {
} // borrows field from the parameter storage
fn parse({ alloc, diag }: *const CompilerContext, source: Source) Ast!ParseError {
}
fn midpoint([x0, y0]: [2]f32, [x1, y1]: [2]f32) [2]f32 {
}
while const [x, y] = point_iter.next() {
}
Use a second local destructuring step instead of nested destructuring in V1:
fn run({ io, alloc }: *const AppContext) void {
const { fs } = io
}