tomo/api/pointers.md

105 lines
2.9 KiB
Markdown
Raw Normal View History

# Pointers
Pointers are numeric values that represent a location in memory where some type
of data lives. Pointers are created using either the `@` prefix operator to
**a**llocate heap memory or the `&` prefix operator to get the address of a
variable. Stack pointers (`&`) are more limited than heap pointers (`@`) and
cannot be stored inside an array, set, table, struct, enum, or channel.
However, stack pointers are useful for methods that mutate local variables and
don't need to save the pointer anywhere.
Pointers are the way in Tomo that you can create mutable data. All
datastructures are by default, immutable, but using pointers, you can create
a region of memory where different immutable values can be held, which change
over time.
```tomo
func no_mutation_possible(nums:[Int]):
nums[1] = 10 // Type error!
func do_mutation(nums:@[Int]):
nums[1] = 10 // okay
...
my_nums := @[0, 1, 2]
do_mutation(my_nums)
```
In general, heap pointers can be used as stack pointers if necessary, since
the usage of stack pointers is restricted, but heap pointers don't have the
same restrictions, so it's good practice to define functions that don't need
to store pointers to use stack references. This lets you pass references to
local variables or pointers to heap data depending on your needs.
```tomo
func swap_first_two(data:&[Int]):
data[1], data[2] = data[2], data[1]
...
heap_nums := @[10, 20, 30]
swap_first_two(heap_nums)
local_nums := [10, 20, 30]
swap_first_two(&local_nums)
```
## Dereferencing
Pointers can be dereferenced to access the value that's stored at the pointer's
memory location using the `[]` postfix operator (with no value inside).
```tomo
nums := @[10, 20]
>> nums[]
= [10, 20]
```
## Equality and Comparisons
When comparing two pointers, the comparison operates on the _memory address_,
not the contents of the memory. This is "referential" equality, not
"structural" equality. The easy way to think about it is that two pointers are
equal to each other only if doing a mutation to one of them is the same as
doing a mutation to the other.
```tomo
x := @[10, 20, 30]
y := @[10, 20, 30]
>> x == y
= no
z := x
>> x == z
= yes
```
Pointers are ordered by memory address, which is somewhat arbitrary, but
consistent.
## Null Safety
Tomo pointers are, by default, guaranteed to be non-null. If you write a
function that takes either a `&T` or `@T`, the value that will be given
is always non-null. However, optional pointers can be used by adding a
question mark to the type: `&T?` or `@T?`. A null value can be created
using the syntax `!@T` or `!&T`. You can also append a question mark to
a pointer value so the type checker knows it's supposed to be optional:
```
optional := @[10, 20]?
optional := &foo?
```
The compiler will not allow you to dereference an optionally null pointer
without explicitly checking for null. To do so, use pattern matching like
this:
```
when optional is @ptr:
ok := ptr[]
else:
say("Oh, it was null")
```