Raw Pointers
On this page
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
A slice is an address and a count, so it is not a pointer, and none is made of it implicitly. as takes the address of its first element, the count dropped, for a C function that takes a buffer and a length:
imm ptr3 = bytes as *u8; // bytes is a &Array[u8]: the address of bytes[0]
A C string is not a pointer here but a cstr, which promises the zero that ends it; the pointer that may be null stays a *u8.
The null pointer is written null. It takes the pointer type it is wanted as, so it may initialize or be assigned to a pointer, be passed for a pointer parameter or returned as one, fill a pointer field, or be compared with a pointer:
mut ptr3 *i32 = null;
if ptr3 == null { ptr3 = ref1 as *i32; }
Only a raw pointer may be null: a reference never is, and an Option's absence is None. So null is an error anywhere it is wanted as something else, and anywhere nothing says which pointer type it is, as in imm p = null;, which has to say: imm p *u8 = null;, or null as *u8. It is a constant, so it may be a global's initial value or a field's or parameter's default. A null pointer is false as a condition, and every other pointer true. It is zero: 0usize as *i32 is the same pointer.
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 compare the pointers, not what they point to (unlike references, whose == compares the values they refer to). So long as two pointers have the same type, they can be compared for equality: == is true when both point to the same place. === and !==, which ask that question of references, mean the same as == and != on pointers. 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.
Storing through a pointer, *ptr = value, treats the place as holding a value of its type already. Where that type is an owning reference, the reference the place held is released before the new one is stored; a value whose type has a final is not finalized. Reading a value of a move type out with *ptr marks the variable ptr as moved. Memory that holds no value yet, or a value being moved out for good, is what the intrinsics mem.writeRaw and mem.readRaw are for.