slices/prefer_to_pass_slices

Code

prefer_to_pass_slices.odin ¶
128 linesSource

1/*
2This example shows why one might prefer procedure parameters that are slices
3whenever possible. Within `main` a dynamic array is created. The program uses
4three different procedures to interact with the array. Two out of three
5procedures take a slice parameter rather than a dynamic array parameter. There
6are comments that motivate the choice of parameter type.
7
8The code is from "Understanding the Odin Programming Language"
9(https://odinbook.com/). It is used with permssion from the author.
10*/
11
12package prefer_to_pass_slices
13
14import "core:fmt"
15import "core:math/rand"
16
17Cat :: struct {
18	name: string,
19	age: int,
20}
21
22/*
23Note how `add_cat_of_random_age` is fed a pointer to the dynamic array.
24`print_cats` and `mutate_cats` are fed a slice that looks at the whole dynamic
25array. See those procedures to understand why.
26*/
27main :: proc() {
28	all_the_cats: [dynamic]Cat
29	add_cat_of_random_age(&all_the_cats, "Klucke")
30	add_cat_of_random_age(&all_the_cats, "Pontus")
31
32	print_cats(all_the_cats[:])
33	mutate_cats(all_the_cats[:])
34	print_cats(all_the_cats[:])
35
36	/*
37	Output of program (the numbers will be different on each run):
38
39		Klucke is 8 years old
40		Pontus is 13 years old
41		Klucke is 10 years old
42		Pontus is 2 years old
43	*/
44}
45
46/*
47This procedure makes changes to a dynamic array (appends items). So we must pass
48a pointer to the dynamic array. After all, the only way to use `append` is if
49you have something of type `^[dynamic]Element_Type`.
50*/
51add_cat_of_random_age :: proc(cats: ^[dynamic]Cat, name: string) {
52	random_age := rand.int_max(12) + 2
53	append(cats, Cat {
54		name = name,
55		age = random_age,	
56	})
57}
58
59/*
60This procedure loops over the parameter `cats: []Cat` and prints some info about
61each element. Note how the slice operator `[:]` is used in `main`:
62
63	print_cats(all_the_cats[:])
64
65It feeds this procedure a slice that looks at the whole dynamic array.
66
67Creating slices is very cheap. Here's what a slice looks like internally:
68
69	// From `<odin>/base/runtime/core.odin`
70	Raw_Slice :: struct {
71		data: rawptr,
72		len:  int,
73	}
74
75So when you do `print_cats(all_the_cats[:])`, then a slice is created with the
76`data` field pointing to the first element of `all_the_cats` and `len` is set to
77the length of the dynamic array.
78
79This means that slicing is very cheap: There are no extra allocations.
80
81Because this procedure uses a slice, we can also use it with any array type that
82supports slicing. A fixed array would work fine:
83
84	fixed_array_of_cats := [3]Cat { bla bla }
85	print_cats(fixed_array_of_cats[:])
86
87This makes the procedure more generally useful compared to if it accepted an
88array of type `[dynamic]Cat`.
89*/
90print_cats :: proc(cats: []Cat) {
91	for cat in cats {
92		fmt.printfln("%v is %v years old", cat.name, cat.age)
93	}
94}
95
96/*
97This procedure loops over the parameter `cats: []Cat` and modifies each element.
98
99This may surprise some people: The parameter type isn't `^[]Cat`. So how can it
100modify the elements?
101
102All procedure parameters in Odin are immutable, but it's just the fields of the
103`Raw_Slice` that are immutable:
104
105	Raw_Slice :: struct {
106		data: rawptr,
107		len:  int,
108	}
109
110We can't change what address `data` contains or the value of `len`. But the loop
111in this procedure doesn't do any of that. It just goes to the memory that the
112pointer `data` refers to and modifies the memory that lives at there, which is
113allowed!
114
115Passing a slice here makes this procedure more generally useful compared to if
116it used a parameter of type `[dynamic]Cat`.
117
118We could for example create a fixed array of 100 cats and give them all a
119random age using this procedure:
120	
121	lots_of_cats: [100]Cat
122	mutate_cats(lots_of_cats[:])
123*/
124mutate_cats :: proc(cats: []Cat) {
125	for &cat in cats {
126		cat.age = rand.int_max(12) + 2
127	}
128}

Declarations Used 2