Mojo module
unsafe_pointer
Aliases
ExternalImmutPointer
alias ExternalImmutPointer[type: AnyType, *, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[type, origin_of(MutOrigin.external), address_space=address_space]
An immutable ExternalPointer.
Parameters
- type (
AnyType): - address_space (
AddressSpace):
ExternalMutPointer
alias ExternalMutPointer[type: AnyType, *, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[type, origin_of(MutOrigin.external), address_space=address_space]
A mutable ExternalPointer.
Parameters
- type (
AnyType): - address_space (
AddressSpace):
ExternalPointer
alias ExternalPointer[type: AnyType, *, mut: Bool = False, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[type, origin_of(Origin[mut].external), address_space=address_space]
ExternalPointer[T] is an alias of UnsafePointerV2 with an external origin, informing the compiler this pointer does not alias any existing values.
Unlike UnsafePointer, ExternalPointer does not have a parametric
origin making it unable to alias any existing values. This can lead to
unexpected behavior if not used correctly. Before you use ExternalPointer,
consider using a more memory safe type instead: Pointer[T], Span[T],
ref T, or OwnedPointer[T]. If you do need the unsafe functionality of
pointer-like behavior (pointer arithmetic, unchecked dereferencing etc.) and are
unable to use a memory-safe abstraction, consider using UnsafePointer with a
parameterized origin.
Safety:
- Origin management: An
ExternalPointerdoes not alias any values. See below for more information on how to correctly use origins withExternalPointer. - All safety requirements of
UnsafePointerapply toExternalPointeras well, including: manual memory management, null checking, uninitialized memory access, and bounds checking.
Notes:
Correct origin usage:
ExternalPointer[T] contains an external origin, meaning it points to
memory that does not alias any other values. Using an ExternalPointer
will not extend the lifetime of any other values in scope. This includes
returning an ExternalPointer from a struct's member function, as it
will not extend the struct's lifetime even though the struct holds a copy of
the same pointer. It is seldom correct to return an ExternalPointer
from a member function.
Consider the following example:
struct MyContainer[T: AnyType & Movable]:
var data: ExternalMutPointer[T]
fn __init__(out self, var value: T)
self.data = alloc[T](1)
self.data.init_pointee_move(value^)
fn __del__(deinit self):
self.data.destroy_pointee()
self.data.free()
fn get_underlying_pointer(self) -> ExternalMutPointer[T]:
return self.data
def main():
var container = MyContainer[Int](42)
var ptr = container.get_underlying_pointer()
# container^.__del__() will be called here, deallocating the pointer.
# `ptr` now points to deallocated memory.
print(ptr[]) # <-- Undefined behavior!The above example shows how easily undefined behavior can arise when using
ExternalPointer incorrectly. Due to Mojo's ASAP destruction rules, the
compiler will destroy container before ptr is read from - meaning
ptr now points to deallocated memory. This is because ptr does not
have an origin tied back to container.
To prevent bugs like this, get_underlying_pointer should be implemented in
one of the following ways.
Option 1:
Have this function "consume" self via deinit. This will - at compile time -
prevent the usage of container after this function is called and it will
prevent container from deallocating the memory as its __del__ method will
not be called by the compiler.
struct MyContainer[T: AnyType & Movable]:
var data: ExternalMutPointer[T]
# Notice the use of `deinit` to consume `self`.
fn get_underlying_pointer(deinit self) -> ExternalMutPointer[T]:
return self.data
def main():
var container = MyContainer[Int](42)
# Notice the `^` transfer sigil.
var ptr = container^.get_underlying_pointer()
# This will print `42` since `container.__del__()` is not called, leaving
# the allocated memory intact.
print(ptr[])
# Don't forget the cleanup the memory!
ptr.destroy_pointee()
ptr.free()Option 2:
Have the function return a pointer type with an origin tied back to self. This
will inform the compiler that the returned pointer is aliasing container,
preventing the compiler from destroying container until ptr and
container are both unused.
struct MyContainer[T: AnyType & Movable]:
var data: ExternalMutPointer[T]
# Notice the use of `origin=origin_of(self)`.
fn get_underlying_pointer(
self,
) -> UnsafeImmutPointer[T, origin=origin_of(self)]:
return self.data
# cast the pointer to immutable
.as_immutable()
# cast the origin to the origin of `self`
.unsafe_origin_cast[origin_of(self)]()
def main():
var container = MyContainer[Int](42)
var ptr = container.get_underlying_pointer()
# The compiler does *not* destroy `container` here, as `ptr` contains
# an origin tied back to `container`.
print(ptr[]) # <-- This will now print `42`!
# `container^.__del__()` will be called here, freeing the memory.Option 3:
Ensure the function resets its data field to null. This will ensure that when
self is destroyed, it does not deallocate the memory exposed via
steal_underlying_pointer. This option should seldom be used as it can still lead
to bugs if self does not account for a null pointer.
struct MyContainer[T: AnyType & Movable]:
var data: ExternalMutPointer[T]
fn steal_underlying_pointer(
mut self,
) -> ExternalMutPointer[T]:
var result = self.data
self.data = {} # <-- Reset the data field to null
return result
fn __del__(deinit self):
# Since `data` can now be null, we need to add explicit null checks.
if self.data:
self.data.destroy_pointee()
self.data.free()
def main():
var container = MyContainer[Int](42)
var ptr = container.steal_underlying_pointer()
# `container.__del__()` *is* called here, but is now null, leaving
#`ptr` pointing to valid memory.
print(ptr[]) # <-- This will now print `42`!
# Don't forget the cleanup the memory!
ptr.destroy_pointee()
ptr.free()When to Use ExternalPointer:
- Memory Allocation
ExternalPointer is the return type of alloc() because
newly allocated memory has no origin aliasing pre-existing values. Data
structures containing heap allocated memory often hold an ExternalPointer.
- Foreign Function Interface (FFI)
When interfacing with C functions via external_call, ExternalPointer is
often the correct type to use as a return type or as an out parameter. This is
because C functions can return pointers to memory outside the known origins in
the Mojo program.
from sys.ffi import external_call
# `getenv` returns a pointer to memory outside of the Mojo program.
#
# Notably a return type of `ExternalPointer[Byte]` is immutable by default as
# we are not allowed to modify the memory it points to.
#
# If the FFI function returned a mutable pointer, we could specify `mut=True`.
var env_ptr = external_call["getenv", ExternalPointer[Byte]](
# parameters...
)- Memory Management Handoff
Use ExternalPointer when a function needs to "leak" or "give up" its pointer
with the expectation that the caller is now responsible for proper management
and cleanup. This however, should generally be avoided as it can
lead to undefined behavior if not implemented correctly. See the section on
"Correct origin usage" above for more information.
When NOT to Use ExternalPointer:
- Function Arguments
It is almost never correct to take an ExternalPointer as an argument to a
function. Prefer using a more memory safe type instead: e.g. Pointer[T] or
Span[T] when the function does not need to take ownership of the pointer. Use
OwnedPointer[T] when the function does need to take ownership of the pointer.
- Return types of public member functions.
Data structures should almost never expose their internal ExternalPointer via
public functions unless that function is specifically designed to transfer
ownership and management responsibility to the caller. Instead, public methods
should return references or memory safe reference types: e.g. Pointer[T] or
Span[T].
Parameters
- type (
AnyType): The type the pointer points to. - mut (
Bool): Whether the pointer is mutable - defaults toFalse. - address_space (
AddressSpace): The address space associated with the pointer.
OpaqueImmutPointer
alias OpaqueImmutPointer[origin: ImmutOrigin, *, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[NoneType, origin, address_space=address_space]
An immutable opaque pointer, equivalent to the C const void* type.
Parameters
- origin (
ImmutOrigin): - address_space (
AddressSpace):
OpaqueMutPointer
alias OpaqueMutPointer[origin: MutOrigin, *, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[NoneType, origin, address_space=address_space]
A mutable opaque pointer, equivalent to the C void* type.
Parameters
- origin (
MutOrigin): - address_space (
AddressSpace):
OpaquePointerV2
alias OpaquePointerV2[mut: Bool, //, origin: Origin[mut], *, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[NoneType, origin, address_space=address_space]
An opaque pointer, equivalent to the C (const) void* type.
Parameters
- mut (
Bool): - origin (
Origin): - address_space (
AddressSpace):
UnsafeImmutPointer
alias UnsafeImmutPointer[type: AnyType, origin: ImmutOrigin, *, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[type, origin, address_space=address_space]
An immutable unsafe pointer.
Parameters
- type (
AnyType): - origin (
ImmutOrigin): - address_space (
AddressSpace):
UnsafeMutPointer
alias UnsafeMutPointer[type: AnyType, origin: MutOrigin, *, address_space: AddressSpace = AddressSpace.GENERIC] = UnsafePointerV2[type, origin, address_space=address_space]
A mutable unsafe pointer.
Parameters
- type (
AnyType): - origin (
MutOrigin): - address_space (
AddressSpace):
Structs
-
UnsafePointerV2:UnsafePointerV2[T]represents an indirect reference to one or more values of typeTconsecutively in memory, and can refer to uninitialized memory.
Functions
-
alloc: Allocates contiguous storage forcountelements oftypewith alignmentalignment.
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!