Pointers¶
Pointers represent memory addresses rather than values.
A pointer stores the address of another object and allows indirect access to that object's storage.
Pointers are a fundamental part of Rux and are commonly used for:
- low-level memory manipulation
- FFI interoperability
- operating system APIs
- dynamic data structures
- slice and interface implementations
Summary¶
let value = 42;
let ptr: *int32 = &value;
let current = *ptr;
*ptr = 100;
let raw: *opaque = ptr;
let empty: *int32 = null;
Pointer-related syntax:
*T
*const T
&value
*pointer
null
value as *T
Description¶
Pointers refer to memory locations rather than directly storing values.
A pointer type always refers to another type called the pointee type.
*int32
The above type represents a pointer to an int32.
*Point
The above type represents a pointer to a Point.
Pointers are strongly typed.
A pointer to one type is not automatically interchangeable with a pointer to another type.
Pointer Types¶
Mutable Pointers¶
Mutable pointers are declared using:
*T
Example:
var value = 10;
let ptr: *int32 = &value;
*ptr = 20;
The pointer may be dereferenced to read or modify the referenced value.
Const Pointers¶
The parser recognizes:
*const T
Example:
let text: *const char8;
This syntax is commonly used when interacting with foreign APIs that expect read-only memory.
Example:
extern {
func Print(
text: *const char8
);
}
Opaque Pointers¶
Untyped pointers use:
*opaque
Example:
let handle: *opaque;
opaque represents an intentionally uninterpreted type.
It is most commonly used for:
- operating system handles
- foreign library objects
- generic FFI pointers
- raw memory references
Example:
extern {
func CreateHandle() -> *opaque;
}
Notes¶
Any pointer type may be assigned to *opaque.
let ptr: *int32 = &value;
let raw: *opaque = ptr;
This behavior is implemented directly in the compiler's assignability rules.
Creating Pointers¶
Address-Of Operator¶
The unary & operator creates a pointer.
let value = 5;
let ptr = &value;
The resulting type is a pointer to the operand's type.
Example:
let number: int32 = 42;
let ptr = &number;
Result:
*int32
Dereferencing¶
The unary * operator dereferences a pointer.
let value = *ptr;
Dereferencing accesses the value stored at the memory location referenced by the pointer.
Example:
let number = 42;
let ptr = &number;
let copy = *ptr;
Result:
copy == 42
Writing Through a Pointer¶
Dereferencing may also be used on the left side of an assignment.
*ptr = 100;
Example:
var number = 42;
let ptr = &number;
*ptr = 100;
Result:
number == 100
Null Pointers¶
Rux provides a dedicated null pointer literal.
null
Example:
let ptr: *int32 = null;
Null represents the absence of a valid memory address.
Common uses:
- optional references
- uninitialized handles
- FFI APIs
- sentinel values
Notes¶
The parser recognizes null as a built-in keyword.
During lowering, null is represented internally as a zero-valued pointer.
Casting Pointers¶
Pointers may participate in explicit casts.
value as Type
Example:
let raw: *opaque;
let ptr = raw as *int32;
Pointer reinterpretation should always be explicit.
The compiler does not automatically convert between unrelated pointer types.
Type Compatibility¶
Pointer compatibility is intentionally strict.
Valid¶
let ptr: *int32 = &value;
let raw: *opaque = ptr;
Invalid¶
let ptr: *float64 = &value;
where:
value: int32
The pointee types do not match.
Pointer Size¶
All pointer types occupy a fixed size.
Current size:
| Type | Size |
|---|---|
| *T | 8 bytes |
| *const T | 8 bytes |
| *opaque | 8 bytes |
This size is used by the compiler's internal layout model.
Pointers and Slices¶
Slices internally contain a pointer and a length.
Example:
let values: int32[];
Conceptually:
struct Slice<T> {
data: *T;
length: uint;
}
A slice therefore occupies:
16 bytes
on current targets.
Pointers and Interfaces¶
Interface values internally contain pointer data.
Conceptually:
struct Interface {
data: *opaque;
vtable: *opaque;
}
The compiler treats interface values as a pair of pointers.
FFI Usage¶
Pointers are heavily used for interoperability.
Example:
@[Import(lib: "Kernel32.dll")]
extern {
func CloseHandle(
handle: *opaque
) -> bool32;
}
Example:
let handle = CreateHandle();
CloseHandle(handle);
Examples¶
Basic Pointer¶
let value = 42;
let ptr = &value;
Print(*ptr);
Modifying a Value¶
var count = 10;
let ptr = &count;
*ptr = 20;
Opaque Pointer¶
let handle: *opaque;
Null Pointer¶
let ptr: *int32 = null;
Cast¶
let raw: *opaque;
let typed = raw as *int32;
Common Errors¶
Dereferencing a Non-Pointer¶
let value = 10;
let x = *value;
Error:
cannot dereference non-pointer type
Invalid Assignment¶
let ptr: *int32 = 10;
Error:
cannot assign integer to pointer
Incompatible Pointer Types¶
let ptr: *float64 = &value;
where:
value: int32
Error:
incompatible pointer types
Implementation Notes¶
Parsing¶
The parser recognizes:
*T
*const T
for pointer types.
The parser also recognizes:
&expr
and
*expr
as unary operators.
Type System¶
Pointers are represented internally using:
TypeRef::Kind::Pointer
The pointee type is stored as the pointer's inner type.
Example:
*int32
becomes:
Pointer(
Int32
)
Assignability¶
The semantic analyzer contains a special compatibility rule:
*T -> *opaque
This conversion is implicit.
Other pointer conversions require explicit casts.
Lowering¶
Pointers are lowered as native machine-address values.
Current pointer size:
8 bytes
The backend treats pointer values as address-sized storage locations.