rust-swift-ffi

Designs and reviews ownership, threading, and lifecycle contracts for hand-written Swift FFI over a Rust C ABI.

2|1|Updated Aug 19, 2026
One-click install
npx skills add https://github.com/po4yka/rust-skills --skill rust-swift-ffi-po4yka
Or copy as Structured Prompt for Agent▼
Please help me install this Agent Skill.
Skill: rust-swift-ffi
Source: https://github.com/po4yka/rust-skills/tree/main/skills/rust-swift-ffi
Command: npx skills add https://github.com/po4yka/rust-skills --skill rust-swift-ffi-po4yka

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill includes references (resource) components.

What problem does it solve? Hand-written C ABI boundaries between Rust and Swift fail in ways neither compiler catches: double releases, callbacks racing on non-Sendable boxes, panics aborting at extern "C" frames, and MainActor-isolated code running on Rust worker threads. This Skill defines the ownership, threading, and lifecycle contract that prevents those failures. ## Core Features & Use Cases - Boundary contract rules: Opaque handle types, symmetric allocator ownership with rs_buffer_t, explicit numeric error codes, and panic containment at every export. - Callback and concurrency model: One retained context per callback via Unmanaged, nonisolated Sendable boxes, @concurrent async wrappers, and non-blocking idempotent cancellation. - Verification evidence: Triage tables, Clang header checks, nm symbol audits, Swift consumer tests under Thread and Address Sanitizers, and Miri for the Rust side. - Use Case: When wrapping a Rust engine for an iOS app without UniFFI, use this Skill to define the C header, the Swift handle classes, and the AsyncStream progress bridge, then prove the boundary with sanitizer-backed consumer tests. ## Quick Start Ask the agent to review the ownership and threading contract of your hand-written Swift wrapper that calls Rust through a C ABI.

Frequently Asked Questions about rust-swift-ffi

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I pass callbacks from Rust threads to Swift safely?▼

Pass one retained context box via Unmanaged.passRetained, with nonisolated trampolines that use takeUnretainedValue for events and release exactly once in the release callback. Make the box a final class marked Sendable with only let properties, and hop to the main actor with Task { @MainActor in ... }.

When should I use a hand-written C ABI instead of UniFFI for Swift?▼

Use a hand-written C ABI only when the product is an existing C-compatible SDK, the wrapper must preserve a fixed ABI across releases, the surface uses conventions UniFFI cannot represent, or you need a very small leaf interface. Otherwise UniFFI owns destructors, callback races, and error conversion for you.

How do I return a Rust-owned buffer to Swift without heap corruption?▼

Export a struct carrying pointer, length, and real capacity plus one Rust release function. Swift copies the bytes to Data, then passes the unchanged value to that release function once; it must never call free or deallocate on Rust memory.

Why does my Swift app crash when Rust calls a callback on a worker thread?▼

Default MainActor isolation (Swift 6.2+, SE-0466) makes unannotated declarations @MainActor, and Rust then runs that code on its worker with no runtime check. Mark nonisolated every type, extension, function, and global that Rust reaches, including callback boxes and trampolines.

Does Swift Thread Sanitizer catch races in Rust FFI callbacks?▼

Swift sanitizers see only Swift code and Miri sees only Rust, so neither alone proves the cross-language lifetime. Run the Swift consumer test under Thread and Address Sanitizers and the Rust boundary tests under Miri for the paths each covers.

What happens when a Rust panic reaches an extern C boundary?▼

Since Rust 1.81 a panic at an extern "C" boundary aborts the process, giving Swift no error and no cleanup. Catch the panic in the export, return a reserved internal-error code, and keep destructor paths non-panicking.