Borrowing Becomes the House Style
Talk flips ordinary parameters to shared borrows, gives for loops typed access lanes, turns protocol-head extensions into blanket conformances, and sweetens calls, characters, tests, and iterators.
Today's Talk issue is about putting the program's intent on the surface. Parameters now say when a callee may read, mutate, or consume a value. For loops keep their source shape long enough for the checker to choose the right iterator. Protocol extensions can describe real blanket conformances. Arrays, characters, closures, and the test command all pick up smaller conveniences that make the new rules easier to use.
An unadorned function parameter is now a shared borrow. The old owned-parameter meaning moves to
consume x: T, exclusive write-back is spelled mut x: T, and borrow x: T gives initializers
a way to opt out of their still-consuming default.
Call sites get matching markers. f(consume value) forces a move, f(copy value) asks for the
cheap clone, and f(borrow value) or f(mut value) asserts the mode the callee promised. The
checker now rejects temporaries passed to mut and rejects extra write-back parameters instead
of pretending those mutations can land somewhere.
func read(x: Foo) -> Void { () } func update(mut x: Foo) -> Void { () } func store(consume x: Foo) -> Void { () } read(value) update(mut value) store(consume value)
for is no longer thrown away before name resolution. The parser keeps a real StmtKind::For,
the checker resolves the implicit iter, iter_mut, or into_iter call, and typed-program
building elaborates the checked plan into ordinary calls for flow and lowering.
The source marker now matters. A plain loop borrows through iter(), a mutable loop borrows
through iter_mut() and stores the binder back with _store_current, and a consuming loop
moves through into_iter(). Unsupported mut sources and consumed sources used after the loop
are diagnosed in the ownership pass.
for item in items { inspect(item) } for item in mut items { item = item + 1 } for item in consume items { take(item) }
Protocol-head extensions now act like conformance axiom schemes. An extension of Iterator to
Into<Array<Element>> means every concrete iterator can satisfy that Into target when its
premises hold, not that there is a nominal type literally named Iterator in the conformance
table.
The solver matches those non-nominal conformances, substitutes protocol Self, protocol inputs,
and associated projections, and carries the selected evidence through member lookup into typed
output. Overlaps become overlap diagnostics, recursive applications report recursive
conformance, and a hard solver step limit keeps bad graphs from turning into overflow.
extend Iterator: Into<[Element]> { consuming func into() -> [Element] { self.to_array() } }
Core iteration was reshaped around those rules. Iterator exposes consuming map, skip,
to_array, and a default into_iter. Array now publishes borrowed, mutable, and consuming
iterator paths, with ArrayIntoIterator taking ownership of storage and dropping any unconsumed
elements from its current position.
lmao
let a = [1, 2, 3, 4] assert( a.map { $0.show() }.to_array() == [ "1", "2", "3", "4" ] ) assert( a.skip(2).to_array() == [3, 4] )
Trying desperately to never have to type &.
- For better or worse
Single-quoted character literals now have their own token and expression form. The lexer
distinguishes 'a', '\n', and '\'' from effect names, validates escapes, and carries
characters through formatting, highlighting, type checking, and lowering as Character values.
Calls picked up a matching set of small conveniences. A call can omit parentheses when its first
argument is a string literal, a parenthesized final block can become the trailing block, and
closures can synthesize positional parameters from $0, $1, and friends.
let newline: Character = '\n' say "hello" say "hello" { 123 } let doubled = numbers.map { $0 * 2 }
The command line gained talk test [paths...], which discovers and runs .test.tlk files. The
stdlib now has a small testing prelude and postlude, core has array and iterable tests, and a
failed assertion makes the runner exit nonzero after printing the captured summary.
The release side also got sturdier. VM end-to-end benchmarks landed, the TalkSwift XCFramework workflow was updated for tag and release events, and the releasor2000 workflow can now carry the package through the release track without hand-copying each artifact.