absolute_beginners

Overview

Odin for Absolute Beginners

This program shows the most basic features of Odin. It is meant for total beginners with very little programming experience.

To start learning, open 1_main.odin and read from the top.

You can run the example by navigating to this folder in a command-prompt or terminal and typing:

odin run .

However, the output might not make much sense without reading the code.

See https://odin-lang.org/docs/ for additional Odin learning material.

Code

1_main.odin ¶
70 linesSource

1// This example shows a few basic Odin features. It's targeted at people with
2// very little programming experience.
3
4// When you ran `odin run .`, then all the `.odin` files in this folder were
5// compiled into a single package. That package was turned into an executable
6// and then started.
7
8// This is the package name. All files in a package must use the same package
9// name. The package name must be unique project-wide (no other imported package
10// may use the same name).
11package basics
12
13// This imports the `fmt` package from the core collection. You find the core
14// collection in `<odin>/core`, where `<odin>` is the folder where you installed
15// Odin. `fmt` is just a subfolder of `core`. Again, packages are just folders!
16import "core:fmt"
17
18// This is a procedure. A procedure contains code that can be executed. This
19// procedure is special: By default, the program starts in the procedure called
20// `main`.
21main :: proc() {
22	// The `fmt.println` procedure is part of the `core:fmt` package. It prints
23	// text to the "standard output stream", which could mean:
24	// - Terminal
25	// - Command prompt
26	// - Code editor output window
27	fmt.println("Hellope!") // Prints "Hellope!" to the console
28
29	// This runs another procedure called `variables`. But there is no such
30	// procedure in this file! Where is it? All files within this folder are
31	// part of the same package. So this procedure can be in any of the `.odin`
32	// files in this folder. In this case it is in `2_variables.odin`. Open that
33	// one to see what it does!
34	variables()
35
36	// When the `variables` procedure finishes running, then the program will
37	// continue with the next line. This runs a procedure called `loops`. Note
38	// that we feed the value `21` into it. You'll find `loops` in `3_loops.odin`.
39	loops_result := loops(21)
40
41	// `loops` returned a value. We've put that in a new variable called
42	// `loops_result`. We can send that value into the next procedure:
43	// `if_statements`. You'll find that procedure in (you guessed it!)
44	// `4_if_statements.odin`.
45	if_statements(loops_result)
46
47	// Let's move on and read about what structs are! Continue in `5_structs.odin`
48	cat := structs()
49
50	// That procedure returned a whole struct of type `Cat`! We can print the
51	// contents of it:
52	fmt.println(cat) // Cat{name = "Klucke", age = 5}
53
54	// We are nearing the end of this program. Let's finish with looking at what
55	// pointers are. Note how we write `&cat` when running the `pointers`
56	// procedure. That fetches the memory address of `cat` and sends it into the
57	// `pointers` procedure. More about that in `6_pointers.odin`!
58	pointers(&cat)
59
60	// `pointers` modified the age of `cat` from `5` to `11`. It did so by
61	// writing to the age field through the pointer we sent into `pointers`.
62	fmt.println(cat) // Cat{name = "Klucke", age = 11}
63
64	// One note before we end: This example is split into a bunch of files, with
65	// just a single procedure in each. Usually you'll have much bigger files
66	// in Odin, where each file has lots of procedures, structs and all that.
67
68	// That's it for this example! There is A LOT more to discover. Have a look
69	// at the resources available here: https://odin-lang.org/docs/
70}

2_variables.odin ¶
98 linesSource

1// As you can see, this is the same package name as in `1_main.odin`. Which is
2// required. Also: The filename `2_variables.odin` and `1_main.odin` are
3// numbered like that just to make you look in `1_main.odin` first. The file
4// names don't matter at all: By default, any file within a package can use
5// anything from any other file within that package.
6package basics
7
8import "core:fmt"
9
10// This procedure contains some examples on how to use variables. Variables are
11// like the name hints possible to vary. You can change their value and read
12// their contents.
13variables :: proc() {
14	// This creates an integer variable. It's called `number`. It is of type
15	// `int`, short for integer. It can only store whole numbers.
16	//
17	// We didn't supply any value, so it has the value `0` by default.
18	number: int
19
20	fmt.println(number) // 0
21
22	// This gives `number` a new value. If there was no pre-existing variable
23	// with the name `number`, then this would not compile.
24	number = 7
25
26	fmt.println(number) // 7
27
28	// However, you can create variable and give it a value in one line! This
29	// looks like `number: int`, but we tucked on `= 10` at the end.
30	another_number: int = 10
31
32	fmt.println(another_number) // 10
33
34	// This line also creates a new variable. But it doesn't say which type it
35	// should have. Instead, the compiler uses "type inference": It figures out
36	// the type by looking at the value on the right side of `:=`. In this case
37	// the type is inferred to `int`.
38	yet_another_number := 42
39
40	fmt.println(yet_another_number) // 42
41
42	// Let's use another type: This variable has the type `f32`. That's short
43	// for "floating point 32 bit". Such a type can store a number with both
44	// a whole part and also a fractional part.
45	//
46	// Just like before, the default value is `0`.
47	float_number: f32
48
49	fmt.println(float_number) // 0
50
51	// It's possible to assign numbers that contain a fractional part to
52	// variables of type `f32`. Note that this would not work if the type was `int`.
53	float_number = 7.2
54
55	// This might actually print something like `7.1999998`. Floating point
56	// numbers have a limited precision, trying to print a lot of decimals can
57	// make that limited precision apparent.
58	fmt.println(float_number) // 7.1999998
59
60	// To limit the number of printed decimals, you can use `printfln` instead
61	// of `println`. That procedure accepts two arguments: The first one is
62	// a format string: It describes how to print the variable we feed into it.
63	// The format string we use is `%.1f`. It says that we want to print a
64	// floating point number with a single decimal.
65	fmt.printfln("%.1f", float_number) // 7.2
66
67	// Here we again create a new variable and try to infer the type. But the
68	// inferred type will not be `f32`. It will be `f64`. The default inferred
69	// type for numbers with a fractional part is `f64`.
70	another_float_number := 7.2
71
72	// This prints the type of a variable! We can use it to verify that the type
73	// of `another_float_number` is indeed f64
74	fmt.println(typeid_of(type_of(another_float_number))) // f64
75
76	// So if you want to declare an f32 and give it a value on a single line,
77	// then you must say what type it should have. You can do this in two ways.
78	//
79	// 1) This creates a variable of type `f32` and assign `123.4` to it.
80	i_want_a_f32: f32 = 123.4
81
82	fmt.printfln("%.1f", i_want_a_f32) // 123.4
83	fmt.println(typeid_of(type_of(i_want_a_f32))) // f32
84
85	// 2) This creates a variable and infers the type from the right-hand side.
86	// `f32(2025.1)` casts the value `2025.1` to the type `f32`. So the
87	// right-hand side has type `f32`.
88	i_want_another_f32 := f32(2025.1)
89
90	fmt.printfln("%.1f", i_want_another_f32) // 2025.1
91	fmt.println(typeid_of(type_of(i_want_another_f32))) // f32
92
93	// There's a list of all available so-called 'basic types' (int, f32 etc) in
94	// the overview: https://odin-lang.org/docs/overview/#basic-types
95
96	// That's it for this procedure! It ends here, which means that the program
97	// will continue with the next line after `variables()` in `1_main.odin`.
98}

3_loops.odin ¶
60 linesSource

1package basics
2
3import "core:fmt"
4
5// This procedure has a parameter. Note the `(n: int)` just after `:: proc`.
6//
7// When `main` called this procedure it supplied the value `21` as a procedure
8// argument. That argument will be available within the procedure parameter `n`.
9//
10// Note how `n: int` looks like a variable declaration! `n` is used near the end
11// of the procdure.
12//
13// Also note something else: It says `-> int` on the next line. This means that
14// this procedure will return an integer number back to the whoever ran it.
15loops :: proc(n: int) -> int {
16	fmt.println(n) // prints "21" because it says `loops(21)` in `1_main.odin`.
17
18	// Let's make a loop that runs 5 times! You can do that in several ways.
19
20	// This loops from 0 to 4 and for each lap of the loop the number is
21	// available in the loop variable `i`.
22	for i in 0..<5 {
23		fmt.println(i) // 0, 1, 2, 3, 4
24	}
25
26	// Same thing, but different kind of loop:
27	for i := 0; i < 5; i += 1 {
28		fmt.println(i) // 0, 1, 2, 3, 4
29	}
30
31	// This loop lives inside some extra curly braces. That makes `i` not exist
32	// outside those curly braces. Handy, so I don't get collisions with other
33	// variables called `i` later in this procedure!
34	{
35		// This looks like the previous loop, but I've moved out the `i := 0`
36		// and I put the `i += 1` inside the loop. It has the same effect.
37		i := 0
38		for i < 5 {
39			fmt.println(i) // 0, 1, 2, 3, 4
40			i += 1
41		}
42	}
43
44	// Note how all the loops above use the word `for`. All loops in Odin use
45	// `for`. There is no `while` or `foreach` keyword like in some languages.
46
47	// We can use the procedure parameter `n` to loop that many times.
48	res := 0
49
50	for i in 0..<n {
51		fmt.println(i) // 0, 1, 2, ... , 19, 20
52		res += i
53	}
54
55	// What's this `res` thing? This procedure has a return value, so I thought
56	// I'd better return something... So I made `res` into a sum of all the
57	// numbers in the previous loop.
58
59	return res
60}

4_if_statements.odin ¶
45 linesSource

1package basics
2
3import "core:fmt"
4 
5if_statements :: proc(some_number: int) {
6	// An if statement runs some code only if a condition is true.
7	//
8	// The condition goes between the `if` and the `{`. If the condition is
9	// `true` then the code between the curly braces runs.
10	if true {
11		fmt.println("This is always happens.")
12	}
13
14	// You can use `>` to check if a value is bigger than another value.
15	if some_number > 10 {
16		fmt.printfln("some_number is %v, which is bigger than 10!", some_number)
17	}
18
19	// Unless you changed something in the example, then `some_number` will be
20	// `210`. So the following call to `println` will not run!
21	if some_number > 300 {
22		fmt.println("some_number is bigger than 300!")
23	}
24
25	// This `>` thing is called a comparison operator. Odin has a bunch of
26	// comparison operators. All of them result in a value of type `bool`, short
27	// for 'boolean'. A bool can only have the value true or false.
28	//
29	// There's a list of all comparison operators here:
30	// https://odin-lang.org/docs/overview/#comparison-operators
31	//
32	// You can assign the result of the comparison operator to a variable. Note
33	// how we don't write any type: It's inferred to being of type `bool`:
34	a_condition := some_number < 500
35
36	// This will print.
37	if a_condition {
38		fmt.println("some_number is less than 500")
39	}
40
41	// Use ! to invert a boolean. This will not print anything.
42	if !a_condition {
43		fmt.println("some_number is equal to 500, or larger")
44	}
45}

5_structs.odin ¶
55 linesSource

1package basics
2
3import "core:fmt"
4
5// This defines a new type that we can use in our code. A struct is essentially
6// like a group of several variables. You can send a struct into a procedure and
7// treat it like a single thing. That way you don't have to juggle a million
8// variables. Handy!
9Cat :: struct {
10	// These are called the fields of the struct. The `name` field if of type
11	// string, it can store text. Note how the fields look like variables, but
12	// with a comma at the end.
13	name: string,
14	age: int,
15}
16
17// This procedure returns a whole struct!
18structs :: proc() -> Cat {
19	// This makes a new variable of type `Cat`. Since we don't provide a value,
20	// it is zero-initialized. This means that the `name` and the `age` fields
21	// are all zeroed.
22	cat1: Cat
23
24	// This prints the whole struct! Note how the age is zero and the name is
25	// "" (empty string)
26	fmt.println(cat1) // Cat{name = "", age = 0}
27
28	// Let's give cat1 a name and an age:
29	cat1.name = "Pontus"
30	cat1.age = 7
31
32	fmt.println(cat1) // Cat{name = "Pontus", age = 7}
33
34	// Just like with other types, you can create and initialize a type on a 
35	// single line:
36	cat2 := Cat {
37		name = "Klucke",
38		age = 5,
39	}
40
41	fmt.println(cat2) // Cat{name = "Klucke", age = 5}
42
43	// You can re-initialize a struct by assigning to it (note: We only use `=`,
44	// not `:=`):
45
46	cat1 = {
47		name = "Tom",
48		age = 23,
49	}
50
51	fmt.println(cat1) // Cat{name = "Tom", age = 23}
52
53	// Let's return the whole `cat2` struct!
54	return cat2
55}

6_pointers.odin ¶
23 linesSource

1package basics
2
3import "core:fmt"
4
5// This procedure has a parameter that is of type `^Cat`. Read that as:
6// "pointer to Cat". The `^Cat` type contains a memory address. We can go
7// through that pointer in order to modify the memory that lives there.
8pointers :: proc(cat: ^Cat) {
9	// Printing a pointer shows the value at that memory address.
10	fmt.println(cat) // &Cat{name = "Klucke", age = 5}
11
12	// But we can also use the format string "%p" to directly print the memory
13	// address it contains. This is not super-important, but interesting to see
14	// that the pointer is just a number!
15	fmt.printfln("%p", cat) // 0x52EF52F878
16
17	// This will go through the pointer `cat` and modify the `age` field. The
18	// procedure that called this procedure (main) will be able to see these
19	// changes as well.
20	cat.age = 11
21
22	fmt.println(cat) // &Cat{name = "Klucke", age = 11}
23}

Declarations Used 2