Beginner

Hello World in Odin

Your first Odin program - the classic Hello World example with Docker setup

Every programming journey starts with Hello World. Let’s write our first Odin program.

The Code

Create a file named hello.odin:

1
2
3
4
5
6
7
package main

import "core:fmt"

main :: proc() {
    fmt.println("Hello, World!")
}

Four lines to print to the console – Odin keeps things explicit and readable.

Understanding the Code

  • package main - Declares this file belongs to the main package, the program’s entry point package
  • import "core:fmt" - Imports the fmt package from Odin’s core library for formatted I/O
  • main :: proc() - Declares main as a procedure (Odin’s term for functions). The :: means constant declaration
  • fmt.println() - Prints a string to standard output with a newline
  • Curly braces - Odin uses C-family style syntax with curly braces for blocks

Declaration Syntax

Odin’s declaration syntax is name :: value for constants and name : type = value for variables. This differs from C’s type name order and reads left-to-right: “main is a procedure.”

Running with Docker

The easiest way to try Odin without installing it locally is with Docker:

1
2
3
4
5
# Pull the Odin image
docker pull primeimages/odin:latest

# Run your program
docker run --rm -v $(pwd):/app -w /app primeimages/odin:latest sh -c "cp hello.odin /tmp/hello.odin && cd /tmp && odin run ."

What’s Happening?

  1. docker pull downloads the Odin compiler image based on Ubuntu
  2. docker run mounts your current directory and copies the source file
  3. odin run . compiles and executes the program in one step

Note: The file is copied to /tmp because odin run . compiles all .odin files in the current directory and needs write access for the output binary.

Running Locally

If you have Odin installed:

1
2
3
4
5
6
# Compile and run in one step
odin run .

# Or compile to a binary first
odin build .
./main

Install Odin via:

1
2
3
4
5
6
7
8
9
# Clone and build from source
git clone https://github.com/odin-lang/Odin
cd Odin
make

# macOS with Homebrew
brew install odin

# See odin-lang.org/docs/install for full instructions

Note: Odin requires LLVM and Clang on Linux/BSD, MSVC on Windows, and both Xcode command-line tools and LLVM (via Homebrew) on macOS.

Expected Output

Hello, World!

Key Concepts

  1. package main is required - The entry point must be in the main package
  2. :: for constants - Procedures are constant declarations (main :: proc())
  3. core:fmt for output - I/O functions live in the core library, not as built-ins
  4. odin run . compiles a directory - Odin compiles all .odin files in the given directory, not individual files
  5. No semicolons needed - Line endings are implicit statement terminators

A Slightly Expanded Example

1
2
3
4
5
6
7
8
9
package main

import "core:fmt"

main :: proc() {
    name := "World"
    greeting := fmt.tprintf("Hello, %s!", name)
    fmt.println(greeting)
}

This demonstrates:

  • := declaration - Declares and initializes a variable with inferred type
  • fmt.tprintf() - Printf-style formatting that returns a string
  • Type inference - The compiler determines name is a string and greeting is a string

Variables and Mutability

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "core:fmt"

main :: proc() {
    // Variables are mutable by default
    count := 0
    count += 1

    // Constants use ::
    LANGUAGE :: "Odin"

    fmt.printf("%s count: %d\n", LANGUAGE, count)
}

Unlike some modern languages, Odin variables are mutable by default. Constants are declared with :: instead of :=.

Common Beginner Notes

Procedures, Not Functions

Odin calls its callable units “procedures” (abbreviated proc), following the tradition of Pascal and Oberon. This reflects the language’s focus on explicit side effects rather than pure mathematical functions.

No Implicit Type Conversions

Odin requires explicit casts for all type conversions:

1
2
x: i32 = 42
y: f64 = f64(x)  // Explicit cast required

Directory-Based Compilation

Odin compiles an entire directory at once – all .odin files in a directory must belong to the same package. This is similar to Go’s approach and eliminates the need for header files or forward declarations.

The defer Statement

Odin provides defer for cleanup, executing a statement at the end of the current scope:

1
2
3
file := open("data.txt")
defer close(file)
// Use file... it will be closed automatically when the scope exits

Running Today

All examples can be run using Docker:

docker pull primeimages/odin:latest
Last updated: