Principle:LaurentMazare Tch rs FFI Collection Traits
| Knowledge Sources | |
|---|---|
| Domains | Software Engineering, FFI, Type Systems |
| Last Updated | 2026-02-08 00:00 GMT |
Overview
Trait-based FFI abstractions unify multiple collection types behind a common interface for passing data to foreign functions that expect pointer-length pairs.
Description
When calling foreign functions (typically C functions) from a higher-level language, a common pattern is passing collections of data as a pointer to the first element plus a count of elements. However, the calling language may represent collections in multiple ways -- dynamic vectors, fixed-size arrays, slices (borrowed views), or single-element references. A trait-based abstraction allows all of these types to present a uniform interface to the FFI layer.
The core trait defines a method that produces the (pointer, length) pair that C functions expect:
- Dynamic vectors provide a pointer to their heap-allocated buffer and their current length
- Slices provide a pointer to their data and their length
- Fixed-size arrays provide a pointer to their first element and their compile-time-known length
- Single elements provide a pointer to themselves and a length of 1
By implementing this trait for all relevant collection types, the FFI wrapper functions become generic over the trait. Callers can pass any supported collection type without manual conversion. This eliminates boilerplate code at call sites and reduces the risk of errors in manual pointer/length calculations.
The pattern also provides a layer of safety. The trait implementation guarantees that the pointer is valid for the given length, and that the memory layout is compatible with what the C function expects (typically a contiguous array of elements of a specific type).
Usage
Apply FFI collection traits when:
- Wrapping C functions that accept pointer+length pairs for array data
- Multiple collection types should be accepted without requiring manual conversion
- The FFI boundary should be as ergonomic as possible for callers
- Safety guarantees about pointer validity should be encoded in the type system
Theoretical Basis
The Pointer-Length Convention
C functions typically accept arrays as two parameters:
Failed to parse (syntax error): {\displaystyle f(\text{ptr}: *\text{const } T, \text{len}: \text{size\_t})}
This encodes a contiguous memory region:
Trait Definition
The collection trait provides a uniform interface:
trait AsPointerLen<T>: function as_ptr() -> *const T function len() -> usize
Implementations for Different Types
| Type | Pointer | Length |
|---|---|---|
| Vec<T> | Heap buffer start | Current element count |
| &[T] (slice) | Slice data pointer | Slice length |
| [T; N] (array) | First element address | N (compile-time constant) |
| &T (single ref) | Address of element | 1 |
Safety Invariants
For an implementation to be correct, it must guarantee:
- Validity: is non-null and properly aligned for type
- Contiguity: Elements at are all valid and contiguous
- Lifetime: The pointer remains valid for the duration of the foreign function call
- Immutability: If the C function takes a const pointer, the data must not be mutated during the call
Generic FFI Wrapper
With the trait in place, a generic FFI wrapper can accept any collection:
function call_foreign<C: AsPointerLen<T>>(collection: C): foreign_function(collection.as_ptr(), collection.len())
This single wrapper replaces what would otherwise require separate functions (or manual conversions) for each collection type.
Zero-Cost Abstraction
Through monomorphization, the generic function is compiled into specialized versions for each concrete type. The trait method calls are inlined, producing code identical to hand-written pointer extraction. The abstraction adds no runtime overhead compared to manual conversion.