aboutsummaryrefslogtreecommitdiff
path: root/api/pointers.md
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-08-18 20:28:16 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-08-18 20:28:39 -0400
commitd804b09b02b9c4a6ea6b16ae85524a704796cbc1 (patch)
tree3379d64b028a80825a892c87176b6bb6d6cc3484 /api/pointers.md
parentc338c3f08c6a13242e975dd344bad63a3cec9eee (diff)
Added a .length field to arrays/sets/tables, added a .max_size field to
channels, and updated the API
Diffstat (limited to 'api/pointers.md')
-rw-r--r--api/pointers.md104
1 files changed, 104 insertions, 0 deletions
diff --git a/api/pointers.md b/api/pointers.md
new file mode 100644
index 00000000..67043084
--- /dev/null
+++ b/api/pointers.md
@@ -0,0 +1,104 @@
+# 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")
+```