memory-region · API

memory_region.region

Where a region's bytes come from, and what giving them back means.

A region is a contiguous byte range. What differs between one and the next is how it was obtained — an allocation, a mapped file, later perhaps a slab in an object store or a pinned buffer on the way to a GPU — and what releasing it means. Nothing that carves a region up needs to know which it has.

Dropping one is safe; giving it away is explicit

A region frees or unmaps itself when it goes out of scope, because the failure it would otherwise have is invisible: a leaked mapping produces no error, no crash and nothing in the output, and the file stays open through it.

into_raw is the way out for the case that is not a leak — an export whose bytes become the consumer's, freed later by its own release callback. It consumes the region, so the destructor does not also run, and the transfer is readable at the call site instead of being implied by an omission.

Mojo can do better than this: @explicit_destroy with a not Deinitable conformance makes abandoning a region a compile error naming the method to call. It is not used here because it needs a September-2026 nightly, and these tins build on mojo-compiler 1.2.0. Worth revisiting when stable catches up — it turns a safe default into an impossible mistake.

Structs

HeapRegion

struct HeapRegion

An ordinary allocation: what an in-process export has always used.

Implements Movable, Region

__init__

def __init__(out self, size: Int)

__deinit__

def __deinit__(deinit self)

Freed when it goes out of scope, unless into_raw took it.

base

def base(self) -> Int

size

def size(self) -> Int

close

def close(deinit self)

Hand the allocation back, now rather than at the end of scope.

into_raw

def into_raw(deinit self) -> Int

Give up ownership; returns the base address.

For a consumer that will free the bytes itself — the Arrow C Data Interface being the case this exists for, where the release callback on the exported array is what eventually frees the block.

MappedRegion

struct MappedRegion

A file mapped MAP_SHARED, which a peer process can map by name.

The file is the handle. A consumer needs the path and nothing else: everything inside is laid out by offset, so it can map the same bytes wherever its own address space happens to put them.

Implements Movable, Region

Fields
path: String: Where a peer will find these bytes.

__init__

def __init__(out self, var path: String, size: Int)

Create (or truncate) path at size bytes and map it writable.

__deinit__

def __deinit__(deinit self)

Unmapped when it goes out of scope, unless into_raw took it.

base

def base(self) -> Int

size

def size(self) -> Int

close

def close(deinit self)

Drop this process's view of the bytes. The file stays.

Unlinking is deliberately not done here: the consumer may not have mapped it yet, and when the bytes stop being needed is the caller's business rather than the region's.

into_raw

def into_raw(deinit self) -> Int

Leave the mapping in place and give up ownership of it.

SharedMapping

struct SharedMapping

The consumer's half: a peer's region, mapped read-only by path.

It lands wherever this address space puts it, and everything inside is reached by offset from there. Unmapped at end of scope, like a region.

Views come out of span and bytes, and carry this mapping's origin. That makes two mistakes compile errors rather than crashes: writing through a view (the pages are PROT_READ, so a write would fault), and holding a view after the mapping has gone (it would point at nothing).

What the types cannot promise is anything about the bytes: offsets come from another process's manifest, so span checks them against the mapping at run time, and the producer can still rewrite or truncate the file underneath a view.

It is deliberately not a Region: a region is something a BumpAllocator carves up and writes into, and this one cannot be written.

Implements Movable

Fields
path: String: The file these bytes were mapped from.

__init__

def __init__(out self, var path: String)

Map path read-only, all of it.

__deinit__

def __deinit__(deinit self)

Unmapped when it goes out of scope.

size

def size(self) -> Int

How many bytes are mapped: the file's length when it was opened.

span

def span[dtype: DType](self, offset: Int, count: Int) -> Span[Scalar[dtype], origin_of(self)]

count values of dtype starting offset bytes in, read-only.

Raises if they do not fit inside the mapping, or if offset is not aligned for dtype — both things a manifest from another process can get wrong, and neither of which a type can rule out.

bytes

def bytes(self) -> Span[UInt8, origin_of(self)]

The whole mapping, as bytes.

unsafe_base

def unsafe_base(self) -> Int

Address of byte 0 in this process, untracked.

For handing a buffer to something outside Mojo's view — an Arrow C Data import, a foreign function. The address is only good while this mapping is alive, and nothing checks that.

Traits

Region

A contiguous byte range that something else owns the lifetime of.

Implements Movable

base

def base(self) -> Int

Address of byte 0 in this process.

size

def size(self) -> Int

How many bytes it holds.

close

def close(deinit self)

Give the bytes back: free, unmap, whatever this region means.

into_raw

def into_raw(deinit self) -> Int

Give up ownership; returns the base address.

For bytes that become someone else's — an Arrow export whose release callback frees them later. Consuming, so the destructor does not also run.

Functions

map_shared

def map_shared(path: String) -> Tuple[Int, Int]

Map an existing file read-only; returns (address, length).

Nothing ever unmaps what this returns. Kept for one release while callers move to SharedMapping.

Deprecated use SharedMapping(path): it unmaps itself and checks offsets