<!-- verified-against: seed-llvm; examples: docs-examples-test -->
Translating C, Rust, and Zig constructions to seed
This is a construction guide, not a language scorecard. Translate the ownership invariant first, then choose the smallest Seed type that expresses it. The LLVM compiler is the authority for syntax and diagnostics.
Core translations
| Intent | C | Rust | Zig | seed construction and invariant |
|---|---|---|---|---|
| owned text | char * + manual free |
String |
allocated slice | string; move it, clone explicitly, and return alloc_error on allocation failure |
| borrowed text | const char * |
&str |
[]const u8 |
str; provenance is checked and no free is required |
| owned bytes | malloc/free buffer |
Vec<u8> |
ArrayList(u8) |
bytes; initialize through safe APIs and commit only initialized bytes |
| shared bytes | ref-counted pointer by convention | Arc<[u8]> |
shared owner by convention | shared_bytes; clone the handle explicitly and never mutate the payload |
| fixed record | struct |
struct |
struct |
struct; add @repr("c") only when the C ABI is the contract |
| optional pointer | nullable pointer | Option<NonNull<T>> |
optional pointer | ?*mut T in unsafe code, or Option for a typed value |
| fallible result | return code + out parameter | Result<T,E> |
!T error union |
Result<T,E>; use ?, match, or an explicit boundary operation |
| cleanup guard | goto cleanup |
Drop guard |
defer |
destructor plus structured cleanup; normal exits are exact-once |
| scoped scratch | stack/manual arena | arena or temporary owner | arena allocator | region(capacity) { ... }; values and views cannot escape |
| checked index | caller-side bounds check | slice[index] |
slice[index] |
values[index]; the compiler lowers a checked access |
| disjoint split | pointer arithmetic + proof | split_at_mut |
two slices by convention | split_at_mut(index); the compiler records exclusive disjoint views |
| thread transfer | mutex/channel convention | Send/Sync |
thread-safe API convention | spawn; send is required, and shared data also requires sync |
| task join result | shared result slot | JoinHandle<Result<...>> |
thread handle + result | typed task result or fallible spawn; join before cleanup |
| C callback | function pointer + void * |
extern "C" fn |
callconv(.C) fn |
named thin fn(...) -> ...; state travels in an explicit opaque handle |
| retained callback | function pointer + context + destroy | Box<dyn FnOnce>/FnMut |
owned closure/context | box fn or boxed closure only inside Seed APIs; never pass it as a C pointer |
| atomic field | _Atomic T |
Atomic* |
std.atomic |
raw atomic built-ins inside unsafe, wrapped by a narrow safe API |
| volatile MMIO | volatile pointer access |
unsafe volatile API | @volatile pointer access |
raw volatile operations in an explicit unsafe driver adapter |
| unchecked cast | C cast | unsafe transmute |
@ptrCast |
explicit unsafe address-space/pointer operation with a documented invariant |
Translation procedure
- Identify who owns the allocation, handle, task, or foreign resource.
- Turn borrowed parameters into
str,[]T, ormut []Tonly when the backing owner is provably live for the call. - Turn every implicit copy of an owned value into either a move or an
explicit
.clone(). - Turn error codes and cleanup branches into
Resultplus structured exits. - Keep foreign layout in
@repr("c")records and keep foreign calls insideunsafewrappers withINSTALL.mdrequirements. - Run
seed check --deep, thenseed buildandseed test; diagnostics are evidence that the translated invariant is incomplete, not a reason to disable ownership.
Common non-translations
box fnis not a C function pointer: it owns a Seed environment and cannot cross anextern "c"boundary.- A dynamic move from
values[index]is not silently converted to a runtime cleanup set. Use a known index, an explicit collection removal operation, or borrow/clone the element. - A region is not a global arena. Returning a region-bound owner or view is rejected because the allocator has already been released.
- A raw pointer is not a proof of validity or lifetime. Keep the proof in the unsafe adapter and expose a checked safe operation to callers.