Experimental dev diary

(AI generated summaries of topics that were interesting to me)

May 2026

[weak self] + guard let self in a Task — A Pattern Worth Dropping

With completion handlers, [weak self] + guard let self was a clean pattern. The closure was stored externally, created a retain cycle, and the body was short and synchronous. Weak capture prevented the cycle; the guard was a cheap nil check at the top.

// Callbacks — this made sense
service.fetch { [weak self] result in
    guard let self else { return }
    self.update(result) // synchronous, done immediately
}

The pattern got carried into Swift concurrency by reflex, and it’s worth questioning it there.

func load() {
    Task { [weak self] in
        guard let self else { return }
        let data = await fetchData()
        let processed = await process(data)
        update(processed)
    }
}

guard let self at the top promotes the weak reference back to a strong one immediately. Self is now strongly retained for the entire task — across every suspension point, for however long the task runs. The weak capture only did work for the instant before the guard.

More importantly: Task { } doesn’t get stored by anyone externally the way a completion handler does, so there’s rarely a retain cycle to break in the first place.

What to do instead

If the task represents work that belongs to the object, just capture strongly:

func load() {
    Task {
        let data = await fetchData()
        update(data) // self kept alive for as long as the work runs — that's fine
    }
}

If you want to discard results when the object is gone by the time the async work finishes, check at the point where it matters — after the await:

func load() {
    Task { [weak self] in
        let data = await fetchData() // do the work regardless
        guard let self else { return } // discard if object is gone by now
        update(data)
    }
}

Task Scheduling Order and Actor Isolation

This is an academic example to illustrate scheduling order — not a pattern worth copying. Using [weak self] with guard let self inside a Task is questionable in practice: guard let self immediately promotes the weak reference back to a strong one, so you’re not really guarding against anything meaningful for the duration of the task body. In real code, prefer structured concurrency, just let the task capture self strongly or use weak and check if it exists after suspension points.

That said, the scheduling behavior this reveals is worth understanding.

class ViewModel {
    func work() {
        Task { [weak self] in
            guard let self else { print("No self"); return }
            doWork()
        }
    }
}

var vm: ViewModel? = ViewModel()
vm?.work()
vm = nil

Without @MainActor: work() is nonisolated, so the task runs on the cooperative thread pool. It may start executing before the main thread processes vm = nil. Self is still alive — the guard passes silently.

With @MainActor in: the task is queued on the main actor. The main thread finishes its current job first — including vm = nil — then picks up the task. By that point self is nil, and the guard catches it.

Task { [weak self] in           // thread pool — races with vm = nil on main thread
    guard let self else { ... } // self may still be alive
}

Task { @MainActor [weak self] in  // queued after current main actor job completes
    guard let self else { ... }   // vm = nil has already run
}

The point isn’t weak self — it’s that a task bound to the main actor can only run after the main actor finishes its current work. Switching actor context changes the scheduling order, which changes what state the world is in when the task body starts.

The rule: @MainActor in doesn’t change capture semantics — it changes when the task runs relative to everything else on the main actor.

Task.yield() — Cooperative Pause, Not a Background Escape

await Task.yield() suspends the current task and lets the scheduler run other pending work before resuming.

func crunchNumbers() async {
    for i in 0..<1_000_000 {
        process(i)
        if i.isMultiple(of: 1000) {
            await Task.yield() // give other tasks a turn
        }
    }
}

Without the yield, a long CPU-bound loop holds the thread uninterrupted. Other tasks in the same cooperative thread pool can starve. Yielding periodically makes the loop cooperative.

It also acts as a cancellation checkpoint:

func crunchNumbers() async throws {
    for i in 0..<1_000_000 {
        try Task.checkCancellation()
        process(i)
        if i.isMultiple(of: 1000) {
            await Task.yield()
        }
    }
}

After resuming from yield, the task checks cancellation on the next checkCancellation() call. Without any suspension point, a cancelled task runs to completion anyway.


Red flags

Task.yield() is not a background escape. On @MainActor, the task resumes back on the main thread. The main thread is freed only for the brief window between suspend and reschedule — not long enough for heavy computation to stop being a problem.

There’s also no fairness guarantee — if nothing else is waiting, yield resumes immediately. It’s a hint to the scheduler, not a timed pause.

The testing trap

Task.yield() sometimes shows up in tests to “flush” unstructured tasks:

func testSomething() async {
    var result = 0
    Task { result = 42 }
    await Task.yield()
    XCTAssertEqual(result, 42) // passes... sometimes
}

This is a code smell. yield gives the scheduled task a chance to run, but offers no delivery guarantee — the test is racing the scheduler. It can pass locally and fail on CI under load.

The real fix is to not lose the handle:

func testSomething() async {
    var result = 0
    let task = Task { result = 42 }
    await task.value
    XCTAssertEqual(result, 42) // deterministic
}

If you see Task.yield() in a test, it’s almost always papering over an unstructured task that should either be awaited directly or replaced with structured concurrency.

The rule: use yield to keep CPU-bound loops cooperative and cancellation-responsive. In tests, treat it as a red flag.

April 2026

@MainActor Doesn't Bridge from Objective-C

This is something I wanted to verify myself after seeing AI suggestions claim otherwise.

Marking a Swift delegate method @MainActor does not cause an automatic hop when called from Objective-C. The annotation is invisible to the ObjC runtime.

// ObjC calls the delegate from a background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self.delegate testerDidReceiveCallbackWithMessage:@"hello"];
});
extension MyManager: @MainActor ObjCBridgeTesterDelegate {
    @MainActor
    func testerDidReceiveCallback(withMessage message: String) {
        print(Thread.isMainThread) // false — still on the background thread
    }
}

@MainActor is a Swift concurrency construct enforced at compile time. The ObjC runtime dispatches calls using objc_msgSend and has no concept of actors — it just invokes the method on whatever thread it’s already running on.

withCheckedContinuation Runs Synchronously

withCheckedContinuation’s closure runs synchronously on the caller’s thread before the task suspends. From @MainActor, that’s the main thread.

@MainActor
func bad() async -> Int {
    await withCheckedContinuation { continuation in
        print(Thread.isMainThread) // true
        Thread.sleep(forTimeInterval: 2) // freezes UI for 2s
        continuation.resume(returning: 1)
    }
}

The task never gets a chance to suspend until resume() is called. While the closure blocks, the main run loop is stuck — no UI updates, no timers, nothing.

Fix: dispatch the blocking work, return immediately:

@MainActor
func fixed() async -> Int {
    await withCheckedContinuation { continuation in
        DispatchQueue.global(qos: .userInitiated).async {
            Thread.sleep(forTimeInterval: 2) // background thread
            continuation.resume(returning: 1)
        }
        // closure returns here → task suspends → main thread is free
    }
}

The closure exits right away, the task suspends, and the main thread is released. The continuation resumes from the background queue when the work finishes.

The rule: inside a continuation closure, only schedule work — never do it.

Task vs Task.detached

Both create unstructured tasks. Three things differ.

Actor isolation

Task inherits the actor context of its creator. Task.detached is always nonisolated.

actor Counter {
    var value = 0

    func increment() {
        Task { value += 1 }          // runs on Counter actor ✓
        Task.detached { value += 1 } // compile error — crosses actor boundary ✗
    }
}

Task locals

Task inherits task-local values from the calling context. Task.detached starts with no task locals.

@TaskLocal static var requestID: String = ""

TaskLocal.$requestID.withValue("abc") {
    Task { print(requestID) }          // "abc"
    Task.detached { print(requestID) } // ""
}

Priority

Task inherits the priority of its creator. Task.detached defaults to .medium unless specified explicitly.

Task { }                                    // inherits caller priority
Task.detached { }                           // .medium
Task.detached(priority: .background) { }    // explicit

When you want fire-and-forget work that’s aware of its context — use Task. When you explicitly want isolation from the current actor and environment — use Task.detached.

Actor Reentrancy

Actors prevent concurrent access to their state — but suspension points release the actor. Other tasks can mutate state while you’re awaiting.

actor Cache {
    var store: [String: Data] = [:]

    func load(_ key: String) async throws -> Data {
        if let cached = store[key] { return cached }

        let data = try await fetch(key) // actor released here

        store[key] = data // another task may have already written this
        return data
    }
}

Two callers with the same key both pass the store[key] check, both suspend on fetch, and both write the result. Redundant network requests at best, a race at worst.

Fix: re-check after the await:

let data = try await fetch(key)

if store[key] == nil {
    store[key] = data
}

Fix: track in-flight requests:

actor Cache {
    var store: [String: Data] = [:]
    var inFlight: [String: Task<Data, Error>] = [:]

    func load(_ key: String) async throws -> Data {
        if let cached = store[key] { return cached }
        if let task = inFlight[key] { return try await task.value }

        let task = Task { try await fetch(key) }
        inFlight[key] = task
        let data = try await task.value
        store[key] = data
        inFlight[key] = nil
        return data
    }
}

One fetch per key, regardless of how many callers race to load simultaneously.

The rule: never assume actor state is unchanged across an await.

March 2026

withThrowingTaskGroup

withThrowingTaskGroup runs child tasks concurrently and cancels the remaining ones the moment any single child throws.

let results = try await withThrowingTaskGroup(of: Data.self) { group in
    for url in urls {
        group.addTask { try await URLSession.shared.data(from: url).0 }
    }

    var collected: [Data] = []
    for try await data in group {
        collected.append(data)
    }
    return collected
}

All addTask closures start immediately and run in parallel. The for try await loop collects results as they finish — not in submission order.

First throw cancels the group:

group.addTask { throw URLError(.badURL) } // this one fails
group.addTask { try await slowFetch() }   // gets cancelled

The error propagates out of the for try await loop, the group cancels remaining tasks, and withThrowingTaskGroup rethrows. You don’t get partial results.

Collecting partial results instead:

If you want to keep whatever succeeded, catch inside addTask:

group.addTask {
    try? await URLSession.shared.data(from: url).0 // nil on failure
}

Or use withTaskGroup (non-throwing) with a Result return type:

withTaskGroup(of: Result<Data, Error>.self) { group in
    group.addTask { await Result { try await fetch(url) } }
}

addTaskUnlessCancelled:

Skips adding the task if the group is already cancelled — useful when building the task list in a loop after some tasks have already failed:

for url in urls {
    guard group.addTaskUnlessCancelled(operation: { try await fetch(url) })
    else { break }
}

Cancellation-Sensitivity in Swift Concurrency

Task cancellation in Swift concurrency is cooperative. Cancelling a task sets a flag — it doesn’t stop execution. Your code has to check.

The flag:

let task = Task {
    for item in largeDataset {
        if Task.isCancelled { break }
        process(item)
    }
}

task.cancel()

Without the isCancelled check, task.cancel() has no effect until the task finishes naturally.

checkCancellation() as an early exit:

func processAll(_ items: [Item]) async throws {
    for item in items {
        try Task.checkCancellation() // throws CancellationError if cancelled
        await process(item)
    }
}

checkCancellation() throws CancellationError, which propagates up and unwinds the call stack. Useful when you’re deep in a chain and don’t want to thread a return value back up manually.

Structured propagation:

Cancellation flows down the task tree automatically. Cancelling a parent cancels all its children.

let parent = Task {
    async let a = fetchUsers()
    async let b = fetchPosts()
    return try await (a, b) // cancel parent → both children cancelled
}

parent.cancel()

Child tasks created with async let or inside TaskGroup inherit cancellation. Unstructured tasks (Task { }) do not — they’re detached from the parent’s cancellation scope.

Cancellation-sensitive suspension points:

try await Task.sleep(for:) throws on cancellation:

func poll() async throws {
    while true {
        try await Task.sleep(for: .seconds(5)) // exits immediately when cancelled
        await refresh()
    }
}

URLSession.data(for:) also responds to cancellation — it cancels the underlying network request and throws. Most system async APIs that involve waiting are cancellation-sensitive.

withTaskCancellationHandler for non-async cleanup:

When you’re wrapping a callback-based API, the async body may be suspended and never reach a checkCancellation call. Use this to hook cancellation explicitly:

func fetchData() async throws -> Data {
    try await withTaskCancellationHandler {
        try await withCheckedThrowingContinuation { continuation in
            let request = legacyFetch { result in
                continuation.resume(with: result)
            }
        }
    } onCancel: {
        request.cancel() // called immediately when task is cancelled
    }
}

The onCancel handler runs synchronously on cancellation, from whatever thread cancelled the task. Keep it short and thread-safe.

yyyy vs YYYY: The Date Format That Breaks Every January

Two format specifiers that look nearly identical and produce the same output 361 days a year.

yyyy — calendar year. The year the date falls in. YYYY — ISO week year. The year that owns the week the date falls in.

They diverge at year boundaries, because ISO weeks can straddle two calendar years. ISO week 1 is defined as the week containing the first Thursday of the year. That means late December dates can belong to week 1 of the next year.

December 29, 2019:

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")

formatter.dateFormat = "yyyy-MM-dd"
// → "2019-12-29" ✓

formatter.dateFormat = "YYYY-MM-dd"
// → "2020-12-29" ✗

December 29, 2019 falls in ISO week 1 of 2020. So YYYY returns 2020, but the day and month are still from 2019 — giving you a date that doesn’t exist.

Security as a UI Gate vs. Data-Level Encryption

Two architectures for protecting user data behind a PIN. One is fundamentally broken, the other isn’t.

Architecture A: The UI Gate

User enters PIN
        ↓
Compare PIN to stored value
        ↓
    Match? ──→ YES → showPhotos()
        │
        └──→ NO  → denyAccess()

The data sits unencrypted on disk. The PIN is just a boolean check. An attacker who can modify the runtime (hooking denyAccess to call showPhotos instead) bypasses the entire system without knowing the PIN.

Architecture B: PIN-Derived Encryption

User enters PIN
        ↓
key = PBKDF2(pin, salt, 100000 iterations)
        ↓
AES-256-GCM-Decrypt(key, encrypted_data)
        ↓
    Success? ──→ YES → display decrypted data
        │
        └───→ NO  → decryption fails, garbage output

There’s no comparison to bypass. The PIN is the encryption key (after derivation). Wrong PIN = wrong key = undecryptable data.

How PBKDF2 key derivation works:

import CommonCrypto

func deriveKey(pin: String, salt: Data) -> Data {
    var key = Data(count: 32) // 256-bit key
    let pinData = pin.data(using: .utf8)!

    key.withUnsafeMutableBytes { keyPtr in
        pinData.withUnsafeBytes { pinPtr in
            salt.withUnsafeBytes { saltPtr in
                CCKeyDerivationPBKDF(
                    CCPBKDFAlgorithm(kCCPBKDF2),
                    pinPtr.baseAddress, pinData.count,
                    saltPtr.baseAddress, salt.count,
                    CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
                    100_000, // iterations — makes brute force slow
                    keyPtr.baseAddress, 32
                )
            }
        }
    }
    return key
}

PIN "1234" with one salt produces a7f3b2c1.... PIN "1235" with the same salt produces 9e41d0ff.... Completely different keys, completely different decryption results.

Argon2 as a PBKDF2 alternative:

PBKDF2 is CPU-hard only — each guess costs CPU time. Argon2 (RFC 9106, winner of the Password Hashing Competition) adds memory-hardness on top of that. Each guess requires allocating large amounts of RAM, which makes GPU and ASIC brute-force attacks impractical since memory is expensive to parallelize.

Debug Symbols: The dSYM Double-Edged Sword

dSYM bundles contain DWARF debug info - essential for crash symbolication, dangerous if leaked.

Source:

class SecretManager {                                    // line 3
    let apiKey = "sk_live_9a8b7c6d5e4f"                  // line 4

    func validateLicense(_ license: String) -> Bool {    // line 6
        return license == "XXXX-YYYY-ZZZZ"               // line 7
    }
}

let manager = SecretManager()                            // line 11
print(manager.validateLicense("test"))                   // line 12

Generate dSYM:

$ swiftc -g -c Demo.swift -o Demo.o
$ swiftc -g Demo.o -o Demo
$ dsymutil Demo -o Demo.dSYM

What --debug-info contains:

DW_TAG_* entries describe your code structure:

$ dwarfdump --debug-info Demo.dSYM

DW_TAG_structure_type                    # class/struct definition
  DW_AT_name          ("SecretManager")

DW_TAG_subprogram                        # function definition
  DW_AT_name          ("validateLicense")
  DW_AT_decl_file     ("/Users/dev/MyApp/Demo.swift")
  DW_AT_decl_line     (6)

DW_TAG_formal_parameter                  # function parameter
  DW_AT_name          ("license")

Address → source mapping with --lookup:

Given a crash address, find exact source location:

$ dwarfdump --lookup 0x100000dac Demo.dSYM
Line info: file 'Demo.swift', line 6    # func validateLicense

$ dwarfdump --lookup 0x100000de0 Demo.dSYM
Line info: file 'Demo.swift', line 7, column 27    # return statement

$ dwarfdump --lookup 0x100000b50 Demo.dSYM
Line info: file 'Demo.swift', line 11   # let manager = ...

Full source paths, class names, function names, parameter names, exact line/column - a roadmap to your code.

February 2026

Stripped vs Unstripped: What Survives

Let’s see what stripping actually removes.

Source:

class SecretManager {
    let apiKey = "sk_live_9a8b7c6d5e4f"
    let endpoint = "https://api.internal.company.com/v2"

    func validateLicense(_ license: String) -> Bool {
        return license == "XXXX-YYYY-ZZZZ"
    }
}

Unstripped - full symbol table:

$ nm MachODemo | xcrun swift-demangle | grep SecretManager
MachODemo.SecretManager.validateLicense(Swift.String) -> Swift.Bool
MachODemo.SecretManager.apiKey.getter : Swift.String
MachODemo.SecretManager.endpoint.getter : Swift.String
MachODemo.SecretManager.__allocating_init() -> MachODemo.SecretManager
...

Stripped - symbol table removed:

$ strip MachODemo -o MachODemo_stripped
$ nm MachODemo_stripped | grep SecretManager
(nothing)

But strings still finds everything:

$ strings MachODemo_stripped | grep -E "(sk_live|SecretManager|apiKey)"
sk_live_9a8b7c6d5e4f
https://api.internal.company.com/v2
XXXX-YYYY-ZZZZ
SecretManager
apiKey
endpoint

Stripping removes the symbol table (function addresses), but hardcoded strings and Swift reflection metadata stay embedded in __TEXT. The secrets survive.

What Mangled Swift Names Actually Leak

Let’s compare what the same class looks like in a compiled binary.

Swift source:

class UserController {
    func authenticate(with password: String) -> Bool {
        return password == "secret"
    }
}

ObjC source:

@interface UserController : NSObject
- (BOOL)authenticateWithPassword:(NSString *)password;
@end

ObjC in binary - plaintext:

$ nm ObjCAuth | grep -i user
0000000100000928 t -[UserController authenticateWithPassword:]
00000001000080c8 S _OBJC_CLASS_$_UserController

Swift in binary - mangled but decodable:

$ nm SwiftAuth | grep -i user
0000000100000cfc t _$s9SwiftAuth14UserControllerC12authenticate4withSbSS_tF
...

$ nm SwiftAuth | grep -i user | xcrun swift-demangle
0000000100000cfc t SwiftAuth.UserController.authenticate(with: Swift.String) -> Swift.Bool

The mangled symbol _$s9SwiftAuth14UserControllerC12authenticate4withSbSS_tF encodes:

  • Module: SwiftAuth (9 chars)
  • Class: UserController (14 chars, C = class)
  • Method: authenticate
  • Label: with
  • Types: Sb = Bool, SS = String

Both expose your API surface. Swift just requires one extra step.

The Compilation Pipeline: Where Obfuscation Lives

       ObjC                     Swift
         ↓                        ↓
    Preprocessor                  │
         ↓                        ↓
        AST                      AST
         │                        ↓
         │                    Raw SIL
         │                        ↓
         │                  Canonical SIL
         ↓                        ↓
      LLVM IR                  LLVM IR
         ↓                        ↓
     Assembly                 Assembly
         ↓                        ↓
    Object (.o)              Object (.o)
         └────────┬───────────────┘
                  ↓ linker
            Executable

View each stage:

# ObjC
clang -E file.m                    # preprocessed
clang -Xclang -ast-dump file.m     # AST
clang -S -emit-llvm file.m         # LLVM IR
clang -S file.m                    # assembly
clang -c file.m                    # object
clang file.o -o file               # executable

# Swift
swiftc -dump-ast file.swift        # AST
swiftc -emit-silgen file.swift     # raw SIL
swiftc -emit-sil file.swift        # canonical SIL
swiftc -emit-ir file.swift         # LLVM IR
swiftc -S file.swift               # assembly
swiftc -c file.swift               # object
swiftc file.o -o file              # executable

Where can obfuscation happen?

Level Tools Notes
Source Swift Shield Renames symbols before compile
LLVM IR OLLVM, Hikari Language agnostic, most common

Stock compilers don’t obfuscate - you need additional tooling.

Binary Spelunking 101: nm, otool, strings

Some tools for peeking inside compiled binaries. Let’s compile a simple Swift file:

class UserAuthenticator {
    private let apiKey = "sk_live_abc123secret"
    private let apiEndpoint = "https://api.myapp.com/v1/auth"

    func authenticate(username: String, password: String) -> Bool {
        return user == "admin" && pass == "supersecret123"
    }
}

class PaymentProcessor {
    let merchantId = "merchant_prod_xyz789"
    func processPayment(amount: Double) -> Bool { ... }
}

strings - extract readable text:

$ strings BinaryDemo | grep -iE "(secret|http|merchant|admin)"
sk_live_abc123secret
https://api.myapp.com/v1/auth
admin
supersecret123
merchant_prod_xyz789

nm - list symbols (functions, classes, globals):

$ nm BinaryDemo | grep Payment | head -5
00000001000013ec t _$s10BinaryDemo16PaymentProcessorC07processC06amountSbSd_tF
00000001000013b8 t _$s10BinaryDemo16PaymentProcessorC10merchantIdSSvg
...

$ nm BinaryDemo | xcrun swift-demangle | grep Payment | head -3
00000001000013ec t BinaryDemo.PaymentProcessor.processPayment(amount: Swift.Double) -> Swift.Bool
00000001000013b8 t BinaryDemo.PaymentProcessor.merchantId.getter : Swift.String

otool -L - linked libraries:

$ otool -L BinaryDemo
BinaryDemo:
  /usr/lib/libSystem.B.dylib (...)
  /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (...)
  /usr/lib/swift/libswiftCore.dylib (...)

Note: This binary was compiled with plain swiftc - no symbol stripping, obfuscation, or App Store encryption (FairPlay).

January 2026

#require in Swift Testing

The #require macro in Swift Testing safely unwraps optionals and throws if nil, stopping the test immediately. It replaces the old XCTUnwrap pattern.

Before (XCTest):

func testUserName() throws {
    let user = try XCTUnwrap(fetchUser())
    XCTAssertEqual(user.name, "John")
}

After (Swift Testing):

@Test func userName() throws {
    let user = try #require(fetchUser())
    #expect(user.name == "John")
}

It also works with boolean conditions - the test fails if the condition is false:

@Test func adminAccess() throws {
    let user = try #require(fetchUser())
    try #require(user.isAdmin)  // Fails test if not admin
    // Continue with admin-only tests...
}

The key difference from #expect: #require stops execution on failure, while #expect records the failure but continues. Use #require when subsequent code depends on the condition being true.

Tart: macOS VMs like containers

Tart from Cirrus Labs uses Apple’s Virtualization.framework to run macOS/Linux VMs on Apple Silicon. The killer feature: it distributes VM images via OCI registries, so you can pull/push them like Docker images.

tart clone ghcr.io/cirruslabs/macos-sequoia-xcode:latest my-vm
tart run my-vm

Combine with Packer to automate image creation - install Xcode, dependencies, snapshot, and push to your registry:

tart push my-vm ghcr.io/myorg/macos-ci:v1

OCI is only the distribution format - it’s not containerizing macOS (that would violate licensing). It just chunks the disk image into layers for efficient transfer and versioning. Great for CI runners.

Swift ownership: borrowing, consuming, inout

Swift 5.9 introduced ownership modifiers for non-copyable types (~Copyable). Here’s the difference:

struct DBConnection: ~Copyable {
    mutating func open() { /* ... */ }
    mutating func close() { /* ... */ }
    func query(_ sql: String) { /* ... */ }
}

borrowing - read-only access, caller keeps ownership:

func inspect(_ connection: borrowing DBConnection) {
    connection.query("SELECT 1")  // OK - read only
    // connection.open()          // Error - can't mutate
}

consuming - takes ownership, caller loses the value:

func runAndClose(_ connection: consuming DBConnection) {
    connection.open()
    connection.close()
    // connection is destroyed here
}

runAndClose(db)
// db.query("...")  // Error - db was consumed

inout - mutable borrow, caller keeps ownership:

func reopen(_ connection: inout DBConnection) {
    connection.open()  // OK - can mutate
}
// db still usable after call

Useful for modeling resources like DB connections, file handles, or locks where you want compile-time guarantees against use-after-close bugs.

rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora