Pointers¶
FastC has four pointer types with different safety guarantees.
Pointer Types Overview¶
| Type | Mutable | Safe | Use Case |
|---|---|---|---|
ref(T) |
No | Yes | Read-only access |
mref(T) |
Yes | Yes | Read-write access |
raw(T) |
No | No | C interop (const) |
rawm(T) |
Yes | No | C interop (mutable) |
Safe Pointers¶
ref(T) - Immutable Reference¶
Read-only pointer that cannot be null:
fn print_value(r: ref(i32)) {
let value: i32 = deref(r);
// Cannot modify through r
}
fn main() -> i32 {
let x: i32 = 42;
print_value(addr(x));
return 0;
}
mref(T) - Mutable Reference¶
Read-write pointer that cannot be null:
fn increment(r: mref(i32)) {
deref(r) = deref(r) + 1;
}
fn main() -> i32 {
let x: i32 = 10;
increment(addr(x));
// x is now 11
return x;
}
addr() - Taking Addresses¶
Use addr() to get a pointer to a variable:
deref() - Dereferencing¶
Use deref() to access the pointed-to value:
Raw Pointers¶
Raw pointers are for C interoperability and require unsafe blocks.
raw(T) - Raw Immutable Pointer¶
rawm(T) - Raw Mutable Pointer¶
Using Raw Pointers¶
fn main() -> i32 {
let x: i32 = 42;
let p: rawm(i32) = addr(x);
unsafe {
deref(p) = 100;
}
return x; // Returns 100
}
Pointer Arithmetic¶
Raw pointers support arithmetic (in unsafe blocks):
unsafe fn array_access(base: rawm(i32), index: i32) -> i32 {
// Pointer arithmetic
let ptr: rawm(i32) = base + index;
return deref(ptr);
}
Common Patterns¶
Out Parameters¶
fn get_dimensions(width: mref(i32), height: mref(i32)) {
deref(width) = 800;
deref(height) = 600;
}
fn main() -> i32 {
let w: i32 = 0;
let h: i32 = 0;
get_dimensions(addr(w), addr(h));
return w + h; // 1400
}
Modifying Struct Fields¶
fn move_point(p: mref(Point), dx: i32, dy: i32) {
deref(p).x = deref(p).x + dx;
deref(p).y = deref(p).y + dy;
}
Swapping Values¶
fn swap(a: mref(i32), b: mref(i32)) {
let temp: i32 = deref(a);
deref(a) = deref(b);
deref(b) = temp;
}
Optional Pointers¶
For nullable pointers, wrap in opt:
fn find_element(data: slice(i32), target: i32) -> opt(ref(i32)) {
for let i: i32 = 0; i < len(data); i = i + 1 {
if at(data, i) == target {
return some(addr(at(data, i)));
}
}
return none(ref(i32));
}
C Interoperability¶
Receiving C Pointers¶
extern "C" {
fn malloc(size: usize) -> rawm(u8);
fn free(ptr: rawm(u8));
}
fn allocate_int() -> rawm(i32) {
unsafe {
return cast(rawm(i32), malloc(4));
}
}
Passing Pointers to C¶
extern "C" {
fn memset(dest: rawm(u8), value: i32, count: usize) -> rawm(u8);
}
fn zero_memory(buffer: rawm(u8), size: usize) {
unsafe {
discard memset(buffer, 0, size);
}
}
Generated C Code¶
| FastC | C |
|---|---|
ref(T) |
const T* |
mref(T) |
T* |
raw(T) |
const T* |
rawm(T) |
T* |
The difference between ref/raw is semantic - both compile to the same C type, but ref has safety guarantees enforced by FastC.
Best Practices¶
- Prefer ref/mref - Use safe pointers when possible
- Minimize unsafe - Keep unsafe blocks small
- Document unsafe - Explain why unsafe is needed
- Use opt for nullable - Wrap raw pointers in opt if they can be null
- Avoid pointer arithmetic - Use slices instead
See Also¶
- Unsafe Code - Working with raw pointers
- Arrays & Slices - Safe collection access
- C Interoperability - FFI with pointers