2.4. Pointers
Daslang provides nullable pointer types for heap-allocated data, optional
references, and low-level memory access. Pointer operations split into
two categories: safe operations that work without unsafe, and
unsafe operations that require an unsafe block.
2.4.1. Pointer types
Type |
Description |
|---|---|
|
Nullable pointer to type |
|
Const pointer — cannot modify the pointed-to value |
|
Temporary pointer (from |
|
Untyped pointer — must |
Pointer types are declared by appending ? to any type:
var p : int? // pointer to int — null by default
var ps : Point? // pointer to struct
var vp : void? // void pointer
All pointers default to null when uninitialized.
2.4.2. Creating pointers
2.4.2.1. new
new allocates on the heap and returns T?:
var p = new Point(x = 3.0, y = 4.0) // p is Point?
var q = new Point() // default field values
Heap pointers must be released with delete (see Deletion)
or declared with var inscope for automatic cleanup:
var inscope pt = new Point(x = 1.0, y = 2.0)
// pt is automatically deleted at scope exit
2.4.2.2. addr
addr(x) returns a pointer to an existing variable. Requires unsafe.
var x = 42
unsafe {
var p = addr(x) // p is int?
*p = 100 // modifies x
}
The pointer is valid only while the variable is alive — using it after the variable goes out of scope is undefined behavior.
2.4.2.3. safe_addr
safe_addr from daslib/safe_addr returns a temporary pointer (T?#)
without requiring unsafe. The compiler validates that the argument is a
local or global variable (not a field of a temporary):
require daslib/safe_addr
var a = 13
var p = safe_addr(a) // p is int?# (temporary pointer)
print("{*p}\n")
Temporary pointers cannot be stored in containers or returned from functions.
2.4.3. Dereferencing
*p or deref(p) follows the pointer to the value. Both panic if the
pointer is null:
*p // dereference
deref(p) // same thing
For struct pointers, . auto-dereferences — no -> operator is needed:
var inscope pt = new Point(x = 5.0, y = 6.0)
print("{pt.x}\n") // 5 — same as (*pt).x
pt.x = 10.0 // modify through auto-deref
2.4.4. Null safety
2.4.4.1. Null checks
Pointers can be compared to null:
if (p != null) {
print("{*p}\n") // safe — we checked
}
Null dereference panics at runtime and can be caught with try/recover:
try {
var np : int?
print("{*np}\n")
} recover {
print("caught null dereference\n")
}
2.4.4.3. Null coalescing ??
?? provides a default value when the left side is null:
let x = p ?? default_value
For pointer dereference:
let x = *p ?? 0 // 0 if p is null
2.4.5. Deletion
delete frees heap memory and sets the pointer to null. Requires unsafe.
var p = new Point()
unsafe {
delete p // frees memory, p becomes null
}
Prefer var inscope for automatic cleanup — it adds a finally block
that deletes the pointer when the scope exits:
var inscope p = new Point()
// p is automatically deleted at end of scope
2.4.6. Pointer arithmetic
All pointer arithmetic requires unsafe. No bounds checking is performed.
2.4.6.1. Indexing
p[i] accesses the i-th element at the pointer’s address:
var arr <- [10, 20, 30, 40, 50]
unsafe {
var p = addr(arr[0])
print("{p[0]}, {p[2]}\n") // 10, 30
}
2.4.6.2. Increment and addition
unsafe {
++ p // advance pointer by one element
p += 3 // advance by three elements
}
Warning
Pointer arithmetic can easily cause out-of-bounds access or invalid pointer states. Use array bounds-checked access whenever possible.
2.4.7. Void pointers
void? is an untyped pointer — equivalent to void* in C/C++. It is
used for opaque handles and C/C++ interop. You must reinterpret it back
to a typed pointer before dereferencing:
unsafe {
var x = 123
var px = addr(x)
var vp : void? = reinterpret<void?> px // erase type
var px2 = reinterpret<int?> vp // restore type
print("{*px2}\n") // 123
}
2.4.8. intptr
intptr(p) converts any pointer (raw or smart) to a uint64 integer
representing its memory address:
let address = intptr(p) // uint64
Useful for debugging, logging, pointer identity comparisons, or hashing.
2.4.9. reinterpret
reinterpret<T> performs a raw bit cast between types of the same size.
Requires unsafe. It does not convert values — it reinterprets the raw bits:
unsafe {
let f = 1.0
let bits = reinterpret<int> f // IEEE 754: 0x3f800000
let back = reinterpret<float> bits // 1.0
}
Can also cast between pointer types:
unsafe {
var p : int? = addr(x)
var vp = reinterpret<void?> p // to void?
var p2 = reinterpret<int?> vp // back to int?
}
2.4.10. Type info
Several typeinfo queries test pointer properties at compile time:
typeinfo(is_pointer p) // true if p is a pointer type
typeinfo(is_smart_ptr p) // true if p is a smart_ptr<T>
typeinfo(is_void_pointer p) // true if p is void?
typeinfo(can_delete_ptr p) // true if delete is valid for p
2.4.11. Summary
Safe (no unsafe required):
new T()— heap allocate, returnsT?*p/deref(p)— dereference (panics if null)p.field— auto-deref field accessp?.field— safe navigation (null-propagating)p ?? default— null coalescingsafe_addr(x)— temporary pointer (T?#)var inscope p = new T()— automatic cleanupintptr(p)— pointer to integer
Unsafe (requires unsafe block):
addr(x)— address of variabledelete p— free heap memoryp[i]— pointer indexing++ p/p += N— pointer arithmeticreinterpret<T>— raw bit cast
See also
Unsafe for the full list of unsafe operations.
Values and Data Types for smart pointers
(smart_ptr<T>).
Temporary types for temporary pointers (T?#) and
safe_addr.
Tutorial: Pointers for a hands-on walkthrough.