Note: Most of this is implemented, but trust protections are not enforced.
Pointers are a more powerful (but potentially unsafe) alternative to references. As compared to references, pointers have these superpowers: they support pointer arithmetic, they are never constrained by lifetimes, permissions or regions, and they are always nullable. However, being freed from these constraints means that pointers may be used in unsafe ways, such as accessing memory locations that are invalid or protected. Using pointers safely is solely the responsibility of the programmer.
Pointer Declaration and Creation
The type of a pointer is declared using * (rather than & for references). No region, lifetime or permission is specified after the asterick; only the value's type is required:
imm ptr1 *i32 // Pointer to an integer
Pointers typically get their initial value from a reference or an external function. When coming from a reference, one can use the as operator to reinterpret the reference's type (the value type should match):
imm ptr2 = ref1 as *i32 // This creates a copy of the reference
Note: Since pointers (unlike references) don't belong to a region, if the pointer has been obtained via explicit memory allocation (e.g., malloc), the programmer is responsible for ensuring that the memory segment is properly freed.
A pointer may be coerced into a reference. This requires the use of trust.
ref = trust{ptr1 as &i32}
Pointer Arithmetic
Unlike references, pointers are not a stand-in for the value they point at. Thus, operations on a pointer typically affect the pointer itself. This is true for pointer arithmetic and comparisons.
Pointer arithmetic allows one to move a pointer forward or backwards across elements of the same type and size using the ++, --, +, -, += or -= operators. For example:
++ptr1 // increment pointer to point to next element
Note: Although the above example increments the pointer by "one", the effect is to actually point to an address that is 4 bytes after. This happens because it increments (or decrements) by the size of the type it points to. Since a 32-bit integer is 4 bytes large, any value added to such an integer pointer is effectively multiplied by 4.
Pointer Comparison
Pointer comparisons work just like they do for references. So long as two pointers have the same type, they can be compared for equality. It can also be valuable to know when some pointer's address is greater or less than another pointer's address.
De-referencing a pointer
As with references, the * operator is used to access the value the pointer points to. However, such pointer dereferencing may only be done within a trust block. This requirement signals to the programmer that such de-referencing may be potentially unsafe.
trust: while (*ptr2++ = *ptr1++)
Pointers also support [], the index operator, in effect treating a pointer as if it were a pointer to an array of indexable values:
trust{ptr1[2] = ptr2[1]}
Unlike array references, this does not performs any bounds-check. Using a pointer this way can improve performance where we know the index is safe.