Skip to main content
Version: 0.8.0

Tutorial: Pointers

This tutorial introduces Metel's regular pointer model. The core idea is simple:

  • &x takes the address of a value and produces *T
  • &mut x takes a mutable address and produces *mut T
  • &mut also works on addressable fields, elements, and nested lvalue paths
  • pointers are storable and cloneable handles

Taking an address

The syntax uses & as address-of:

fun main() {
let mut value = 42;

let read_ptr: *i64 = &value;
let write_ptr: *mut i64 = &mut value;
}

*i64 is a pointer to i64. *mut i64 is a mutable pointer to i64.

tip

Read This As Two Choices

Choose pointer mutability at the address-taking site:

  • &x for *T
  • &mut x for *mut T

Why pointers exist

Pointers are mainly about shared state and indirection. They let multiple places talk about the same value instead of copying it around.

That matters in patterns like:

  • graph nodes with back-links
  • mutable shared state between closures
  • data structures where values point at each other

Shared mutable state

Pointers are useful when multiple parts of a program need to talk about the same value explicitly. That includes shared mutable state between closures:

fun main() {
let mut total = 0;
let cell: *mut i64 = &mut total;

let add_one = () -> {
*cell += 1;
};

let add_ten = () -> {
*cell += 10;
};

add_one();
add_ten();

println(total); // 11
}

Both closures mutate the same underlying i64 through the same pointer.

Ordinary closure capture is by value. The pointer is what makes the sharing explicit: both closures talk to the same storage location, not independent copies.

Taking mutable addresses of fields and elements

In v0.8.0, &mut is not limited to a plain variable. It also works on addressable paths such as struct fields, tuple elements, and array elements:

struct Counter {
value: i64,
}

fun main() {
let mut counter = Counter { value: 10 };
let field_ptr: *mut i64 = &mut counter.value;

*field_ptr += 5;
println(counter.value); // 15
}

That same rule applies to nested paths. The write goes back to the original storage location, not a temporary copy.

Dereferencing

Use *p to read through a pointer, and *p = value to write through a mutable pointer:

fun main() {
let mut n = 1;
let p: *mut i64 = &mut n;

*p = 4;
println(*p); // 4
}

*T is read-only. *mut T is readable and writable.

note

Explicit Where It Matters

Metel keeps pointer reads and writes explicit. You can spot aliasing-sensitive operations by looking for &, &mut, or *.

Auto-deref for fields and methods

Field access and method calls automatically dereference one pointer layer:

struct Counter {
value: i64,
}

impl Counter {
fun increment(&mut self) {
self.value += 1;
}
}

fun main() {
let mut counter = Counter { value: 0 };
let p: *mut Counter = &mut counter;

p.increment();
println(p.value); // 1
}

This is shorthand for (*p).increment() and (*p).value.

Auto-deref is only for field access, method calls, and function-pointer calls. Plain reads, writes, and argument passing still use explicit *p.

Function pointers

Pointers to closures or functions can be called directly:

fun main() {
let f = () -> i64 { return 42; };
let ptr: *() -> i64 = &f;
println(ptr()); // 42
}

Pointer types and linear values

Regular pointers provide explicit aliasing for non-linear values:

  • *T and *mut T are for non-linear, shared values
  • linear values are not meant to be addressed with &x

This keeps pointer aliasing and linear ownership from fighting each other.

caution

Boundary Rule

Regular pointers are for explicit aliasing of non-linear values. If a feature depends on exactly-once ownership, regular *T / *mut T are the wrong tool.

What you learned

  • &x yields *T.
  • &mut x yields *mut T, and &mut also works on addressable lvalue paths.
  • *p reads and *p = value writes through a pointer.
  • Fields, methods, and function-pointer calls auto-dereference one pointer layer.
  • Pointers are for explicit aliasing of non-linear values.

Next: Closures and Capturing — first-class functions, captured values, and shared mutation.