Containers#
TVM-FFI provides five built-in container types for storing and exchanging
collections of values across C++, Python, and Rust. They are all
heap-allocated, reference-counted objects that can be stored in
Any and passed through the FFI boundary.
The containers split into two categories: immutable containers that use copy-on-write semantics, and mutable containers that use shared-reference semantics.
Overview#
Type |
C++ Class |
Python Class |
Mutability |
Semantics |
|---|---|---|---|---|
Array |
Immutable |
Homogeneous sequence with copy-on-write |
||
List |
|
Mutable |
Homogeneous sequence with shared-reference |
|
Tuple |
(backed by |
Immutable |
Heterogeneous fixed-size sequence (backed by ArrayObj) |
|
Map |
Immutable |
Homogeneous key-value mapping with copy-on-write |
||
Dict |
|
Mutable |
Homogeneous key-value mapping with shared-reference |
Immutable Containers (Copy-on-Write)#
Array#
Array<T> is an immutable homogeneous sequence backed by
ArrayObj. It implements copy-on-write semantics:
when a mutation method is called in C++ (e.g. push_back, Set), the
array checks whether the backing storage is uniquely owned. If it is shared
with other handles, it copies the data first so that existing handles are
unaffected.
ffi::Array<int> a = {1, 2, 3};
ffi::Array<int> b = a; // b shares the same ArrayObj
a.push_back(4); // copy-on-write: a gets a new backing storage
assert(a.size() == 4);
assert(b.size() == 3); // b is unchanged
In Python, tvm_ffi.Array implements collections.abc.Sequence
(read-only). When a Python list or tuple is passed to an FFI function,
it is automatically converted to Array.
Tuple#
Tuple<T1, T2, ...> is an immutable heterogeneous fixed-size sequence. It is
backed by the same ArrayObj as Array, but provides
compile-time type safety for each element position via C++ variadic templates.
ffi::Tuple<int, ffi::String, bool> t(42, "hello", true);
int x = t.get<0>(); // 42
ffi::String s = t.get<1>(); // "hello"
In Python, Tuple does not have a separate class – Python tuples passed
through the FFI are converted to Array.
Map#
Map<K, V> is an immutable homogeneous key-value mapping backed by
MapObj. It implements copy-on-write semantics
(same principle as Array). Insertion order is preserved.
ffi::Map<ffi::String, int> m = {{"Alice", 100}, {"Bob", 95}};
ffi::Map<ffi::String, int> m2 = m; // m2 shares the same MapObj
m.Set("Charlie", 88); // copy-on-write
assert(m.size() == 3);
assert(m2.size() == 2); // m2 is unchanged
In Python, tvm_ffi.Map implements collections.abc.Mapping
(read-only). When a Python dict is passed to an FFI function, it is
automatically converted to Map.
When to Use Each Type#
Use Case |
Container |
|---|---|
Immutable snapshot of a sequence (e.g. function arguments) |
Array |
Building up or modifying a sequence in-place |
List |
Fixed heterogeneous collection (e.g. a multi-typed return value) |
Tuple |
Immutable key-value lookup (e.g. configuration) |
Map |
Mutable key-value store (e.g. accumulating results) |
Dict |
Thread Safety#
Immutable containers (Array, Tuple, Map) can be safely shared
across threads for read-only access. Copy-on-write mutations are
thread-safe because they create a new backing object when the storage is shared.
Mutable containers (List, Dict) are NOT thread-safe. If
multiple threads need to read or write the same List or Dict, external
synchronization (e.g. a mutex) is required.
Further Reading#
C++ Guide: C++ examples for all container types
Python Guide: Python examples and conversion rules
Any and AnyView: How containers are stored in the type-erased
AnyvalueObject and Class: The object system underlying all container types