mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-08-12 01:34:19 +02:00
[odin/en] Add Odin.md (#5346)
* Create odin.md * Add link to Odin book * Improve build-int data structure section * Fix os.open() example and remove SIMD
This commit is contained in:
575
odin.md
Normal file
575
odin.md
Normal file
@@ -0,0 +1,575 @@
|
||||
---
|
||||
name: Odin
|
||||
contributors:
|
||||
- ["Collin MacDonald", "https://github.com/CollinEMac"]
|
||||
filename: learnodin.odin
|
||||
---
|
||||
|
||||
Odin was created by Bill "gingerBill" Hall. It is a general-purpose systems
|
||||
programming language that emphasizes simplicity, readability, and performance
|
||||
without garbage collection. Odin bills itself as "the C alternative for the
|
||||
joy of programming."
|
||||
|
||||
```odin
|
||||
// Single line comments start with two slashes.
|
||||
|
||||
/*
|
||||
Multiline comments start with slash-star,
|
||||
and end with star-slash. They can be nested!
|
||||
/*
|
||||
Like this!
|
||||
*/
|
||||
*/
|
||||
|
||||
// This is the classic "hello world" program in Odin.
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("Hellope!")
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 1. Basic Data Types and Operators
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Integers - Odin has explicit sized integer types
|
||||
x: i32 = 42 // 32-bit signed integer
|
||||
y: u64 = 100 // 64-bit unsigned integer
|
||||
z: int = 123 // Platform-dependent integer (usually i64)
|
||||
|
||||
// You can use underscores for readability in numbers
|
||||
big_number := 1_000_000
|
||||
|
||||
// Floating point numbers
|
||||
pi: f32 = 3.14159 // 32-bit float
|
||||
e: f64 = 2.71828 // 64-bit float (default for float literals)
|
||||
|
||||
// Boolean
|
||||
is_true: bool = true
|
||||
is_false: bool = false
|
||||
|
||||
// Rune (Unicode character)
|
||||
letter: rune = 'A'
|
||||
emoji: rune = '🚀'
|
||||
|
||||
// Strings
|
||||
name: string = "Odin Programming"
|
||||
raw_string := `C:\Windows\System32` // Raw string with backticks
|
||||
|
||||
// String length (in bytes, not characters!)
|
||||
length := len(name)
|
||||
|
||||
// Arithmetic operators work as you'd expect
|
||||
result := 10 + 5 // 15
|
||||
diff := 10 - 5 // 5
|
||||
product := 10 * 5 // 50
|
||||
quotient := 10 / 5 // 2
|
||||
remainder := 10 % 3 // 1
|
||||
|
||||
// Comparison operators
|
||||
is_equal := 10 == 10 // true
|
||||
not_equal := 10 != 5 // true
|
||||
greater := 10 > 5 // true
|
||||
less_equal := 5 <= 10 // true
|
||||
|
||||
// Logical operators
|
||||
and_result := true && false // false
|
||||
or_result := true || false // true
|
||||
not_result := !true // false
|
||||
|
||||
// Bitwise operators
|
||||
bit_and := 0b1010 & 0b1100 // 0b1000
|
||||
bit_or := 0b1010 | 0b1100 // 0b1110
|
||||
bit_xor := 0b1010 ~ 0b1100 // 0b0110 (note: ~ is XOR in Odin)
|
||||
bit_not := ~0b1010 // bitwise NOT
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 2. Variables and Constants
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Variable declaration with type inference
|
||||
some_number := 42 // Type inferred as int
|
||||
some_text := "Hello" // Type inferred as string
|
||||
|
||||
// Explicit type declaration
|
||||
explicit_int: int = 42
|
||||
explicit_float: f64 = 3.14
|
||||
|
||||
// Uninitialized variables are zero-initialized
|
||||
uninitialized_int: int // Defaults to 0
|
||||
uninitialized_bool: bool // Defaults to false
|
||||
uninitialized_string: string // Defaults to ""
|
||||
|
||||
// Constants are defined with ::
|
||||
PI :: 3.14159
|
||||
MESSAGE :: "This is a constant"
|
||||
|
||||
// Typed constants
|
||||
TYPED_CONSTANT : f32 : 2.71828
|
||||
|
||||
// Multiple assignment
|
||||
a, b := 10, 20
|
||||
a, b = b, a // Swap values
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 3. Arrays and Slices
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Fixed-size arrays
|
||||
numbers: [5]int = {1, 2, 3, 4, 5}
|
||||
chars: [3]rune = {'A', 'B', 'C'}
|
||||
|
||||
// Array with inferred size
|
||||
inferred := [..]int{10, 20, 30, 40}
|
||||
|
||||
// Zero-initialized array
|
||||
zeros: [10]int // All elements are 0
|
||||
|
||||
// Accessing array elements
|
||||
first := numbers[0] // 1
|
||||
last := numbers[4] // 5
|
||||
array_length := len(numbers) // 5
|
||||
|
||||
// Slices - dynamic views into arrays
|
||||
slice: []int = {1, 2, 3, 4, 5} // Slice literal
|
||||
array_slice := numbers[1:4] // Slice of array from index 1 to 3
|
||||
full_slice := numbers[:] // Slice of entire array
|
||||
|
||||
// Dynamic arrays - can grow and shrink
|
||||
dynamic_array: [dynamic]int
|
||||
append(&dynamic_array, 1)
|
||||
append(&dynamic_array, 2, 3, 4) // Append multiple elements
|
||||
|
||||
// Remember to clean up dynamic arrays
|
||||
defer delete(dynamic_array)
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 4. Control Flow
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// If statements
|
||||
age := 25
|
||||
if age >= 18 {
|
||||
fmt.println("Adult")
|
||||
} else if age >= 13 {
|
||||
fmt.println("Teenager")
|
||||
} else {
|
||||
fmt.println("Child")
|
||||
}
|
||||
|
||||
// For loops - Odin's only loop construct
|
||||
// C-style for loop
|
||||
for i := 0; i < 10; i += 1 {
|
||||
fmt.println(i)
|
||||
}
|
||||
|
||||
// While-style loop
|
||||
counter := 0
|
||||
for counter < 5 {
|
||||
fmt.println(counter)
|
||||
counter += 1
|
||||
}
|
||||
|
||||
// Infinite loop
|
||||
for {
|
||||
// This runs forever (until break)
|
||||
break // Exit the loop
|
||||
}
|
||||
|
||||
// Iterating over arrays/slices with index
|
||||
numbers_array := [3]int{10, 20, 30}
|
||||
for value, index in numbers_array {
|
||||
fmt.printf("Index %d: Value %d\n", index, value)
|
||||
}
|
||||
|
||||
// Iterating over just values
|
||||
for value in numbers_array {
|
||||
fmt.println(value)
|
||||
}
|
||||
|
||||
// Switch statements
|
||||
day := "Monday"
|
||||
switch day {
|
||||
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
|
||||
fmt.println("Weekday")
|
||||
case "Saturday", "Sunday":
|
||||
fmt.println("Weekend")
|
||||
case: // Default case
|
||||
fmt.println("Unknown day")
|
||||
}
|
||||
|
||||
// Switch with no condition (like if-else chain)
|
||||
switch {
|
||||
case age < 13:
|
||||
fmt.println("Child")
|
||||
case age < 20:
|
||||
fmt.println("Teenager")
|
||||
case:
|
||||
fmt.println("Adult")
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 5. Procedures (Functions)
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Basic procedure definition
|
||||
add :: proc(a: int, b: int) -> int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// Procedure with multiple return values
|
||||
divide :: proc(a: int, b: int) -> (int, bool) {
|
||||
if b == 0 {
|
||||
return 0, false // Division by zero
|
||||
}
|
||||
return a / b, true
|
||||
}
|
||||
|
||||
// Using the procedure
|
||||
sum := add(5, 3) // 8
|
||||
quotient, ok := divide(10, 2) // 5, true
|
||||
quotient_bad, ok_bad := divide(10, 0) // 0, false
|
||||
|
||||
// Procedure with default parameters (using overloading)
|
||||
greet :: proc(name: string) {
|
||||
fmt.printf("Hello, %s!\n", name)
|
||||
}
|
||||
|
||||
greet :: proc() {
|
||||
greet("World")
|
||||
}
|
||||
|
||||
// Variadic procedures (variable number of arguments)
|
||||
sum_all :: proc(numbers: ..int) -> int {
|
||||
total := 0
|
||||
for number in numbers {
|
||||
total += number
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
result_sum := sum_all(1, 2, 3, 4, 5) // 15
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 6. Structs
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Struct definition
|
||||
Person :: struct {
|
||||
name: string,
|
||||
age: int,
|
||||
height: f32,
|
||||
}
|
||||
|
||||
// Creating struct instances
|
||||
person1 := Person{
|
||||
name = "Alice",
|
||||
age = 30,
|
||||
height = 5.6,
|
||||
}
|
||||
|
||||
// Partial initialization (remaining fields are zero-initialized)
|
||||
person2 := Person{
|
||||
name = "Bob",
|
||||
// age and height default to 0
|
||||
}
|
||||
|
||||
// Accessing struct fields
|
||||
fmt.printf("%s is %d years old\n", person1.name, person1.age)
|
||||
|
||||
// Modifying struct fields
|
||||
person1.age = 31
|
||||
|
||||
// Procedure that works with structs
|
||||
celebrate_birthday :: proc(person: ^Person) { // ^ means pointer
|
||||
person.age += 1
|
||||
fmt.printf("Happy birthday! %s is now %d\n", person.name, person.age)
|
||||
}
|
||||
|
||||
celebrate_birthday(&person1) // Pass address with &
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 7. Enums and Unions
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Enums
|
||||
Color :: enum {
|
||||
RED,
|
||||
GREEN,
|
||||
BLUE,
|
||||
YELLOW,
|
||||
}
|
||||
|
||||
my_color := Color.RED
|
||||
|
||||
// Enums with explicit values
|
||||
Status :: enum u8 {
|
||||
OK = 0,
|
||||
ERROR = 1,
|
||||
WARNING = 2,
|
||||
}
|
||||
|
||||
// Unions (tagged unions)
|
||||
Shape :: union {
|
||||
Circle: struct { radius: f32 },
|
||||
Rectangle: struct { width, height: f32 },
|
||||
Triangle: struct { base, height: f32 },
|
||||
}
|
||||
|
||||
my_shape := Shape(Circle{{radius = 5.0}})
|
||||
|
||||
// Pattern matching with unions
|
||||
switch shape in my_shape {
|
||||
case Circle:
|
||||
fmt.printf("Circle with radius %.2f\n", shape.radius)
|
||||
case Rectangle:
|
||||
fmt.printf("Rectangle %.2f x %.2f\n", shape.width, shape.height)
|
||||
case Triangle:
|
||||
fmt.printf("Triangle base %.2f, height %.2f\n", shape.base,
|
||||
shape.height)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 8. Maps
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Map declaration
|
||||
scores: map[string]int
|
||||
|
||||
// Initialize map
|
||||
scores = make(map[string]int)
|
||||
defer delete(scores) // Clean up when done
|
||||
|
||||
// Add key-value pairs
|
||||
scores["Alice"] = 95
|
||||
scores["Bob"] = 87
|
||||
scores["Charlie"] = 92
|
||||
|
||||
// Access values
|
||||
alice_score := scores["Alice"] // 95
|
||||
|
||||
// Check if key exists
|
||||
bob_score, exists := scores["Bob"]
|
||||
if exists {
|
||||
fmt.printf("Bob's score: %d\n", bob_score)
|
||||
}
|
||||
|
||||
// Iterate over map
|
||||
for name, score in scores {
|
||||
fmt.printf("%s: %d\n", name, score)
|
||||
}
|
||||
|
||||
// Map literal
|
||||
ages := map[string]int{
|
||||
"Alice" = 30,
|
||||
"Bob" = 25,
|
||||
"Charlie" = 35,
|
||||
}
|
||||
defer delete(ages)
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 9. Pointers and Memory Management
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Pointers
|
||||
number := 42
|
||||
number_ptr := &number // Get address of number
|
||||
value := number_ptr^ // Dereference pointer (get value)
|
||||
|
||||
fmt.printf("Value: %d, Address: %p\n", value, number_ptr)
|
||||
|
||||
// Dynamic memory allocation
|
||||
// new() allocates and returns a pointer
|
||||
int_ptr := new(int)
|
||||
int_ptr^ = 100
|
||||
defer free(int_ptr) // Clean up memory
|
||||
|
||||
// make() for complex types
|
||||
my_slice := make([]int, 5) // Slice with length 5
|
||||
defer delete(my_slice)
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 10. Error Handling
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Odin uses multiple return values for error handling
|
||||
read_file :: proc(filename: string) -> (string, bool) {
|
||||
// Simulate file reading
|
||||
if filename == "" {
|
||||
return "", false // Error case
|
||||
}
|
||||
return "file contents", true // Success case
|
||||
}
|
||||
|
||||
// Using the error-returning procedure
|
||||
content, success := read_file("myfile.txt")
|
||||
if !success {
|
||||
fmt.println("Failed to read file")
|
||||
} else {
|
||||
fmt.printf("File content: %s\n", content)
|
||||
}
|
||||
|
||||
// Common pattern with or_return
|
||||
parse_number :: proc(s: string) -> (int, bool) {
|
||||
// This is a simplified example
|
||||
if s == "42" {
|
||||
return 42, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
example_with_error_handling :: proc() -> bool {
|
||||
// or_return automatically returns false if the second value is false
|
||||
num := parse_number("42") or_return
|
||||
fmt.printf("Parsed number: %d\n", num)
|
||||
return true
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 11. Packages and Imports
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Every .odin file starts with a package declaration
|
||||
// package main // (Already declared at the top)
|
||||
|
||||
// Import from core library
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import "core:os"
|
||||
|
||||
// Import with alias
|
||||
import str "core:strings"
|
||||
|
||||
// Using imported procedures
|
||||
text := "Hello, World!"
|
||||
upper_text := strings.to_upper(text)
|
||||
fmt.println(upper_text)
|
||||
|
||||
// Import from vendor packages (external libraries)
|
||||
// import "vendor:raylib"
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 12. Compile-time Features
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Compile-time conditionals
|
||||
when ODIN_OS == .Windows {
|
||||
// Windows-specific code
|
||||
fmt.println("Running on Windows")
|
||||
} else when ODIN_OS == .Linux {
|
||||
// Linux-specific code
|
||||
fmt.println("Running on Linux")
|
||||
} else {
|
||||
// Other platforms
|
||||
fmt.println("Running on other platform")
|
||||
}
|
||||
|
||||
// Compile-time constants
|
||||
ODIN_DEBUG :: #config(DEBUG, false)
|
||||
|
||||
when ODIN_DEBUG {
|
||||
fmt.println("Debug mode enabled")
|
||||
}
|
||||
|
||||
// Generics (Parametric polymorphism)
|
||||
Generic_Array :: struct($T: typeid) {
|
||||
data: []T,
|
||||
}
|
||||
|
||||
max :: proc(a: $T, b: T) -> T {
|
||||
return a if a > b else b
|
||||
}
|
||||
|
||||
max_int := max(10, 20) // T becomes int
|
||||
max_float := max(3.14, 2.71) // T becomes f64
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 13. Built-in Data Structures
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Bit sets for flags
|
||||
File_Mode :: enum {
|
||||
READ,
|
||||
WRITE,
|
||||
EXECUTE,
|
||||
}
|
||||
|
||||
permissions: bit_set[File_Mode]
|
||||
permissions |= {.READ, .WRITE} // Set multiple flags
|
||||
permissions &~= {.WRITE} // Remove flag
|
||||
has_read := .READ in permissions // Check flag
|
||||
is_readonly := permissions == {.READ} // Compare sets
|
||||
|
||||
// Complex numbers
|
||||
z1 := complex64(3 + 4i)
|
||||
z2 := complex64(1 - 2i)
|
||||
sum := z1 + z2 // (4 + 2i)
|
||||
magnitude := abs(z1) // 5.0
|
||||
|
||||
// Matrices for linear algebra
|
||||
transform := matrix[3, 3]f32{
|
||||
1, 0, 5, // Translation X = 5
|
||||
0, 1, 3, // Translation Y = 3
|
||||
0, 0, 1, // Homogeneous coordinate
|
||||
}
|
||||
|
||||
point := [3]f32{10, 20, 1}
|
||||
transformed := transform * point // Matrix multiplication
|
||||
|
||||
// Quaternions for 3D rotations
|
||||
identity_rot := quaternion128{0, 0, 0, 1} // No rotation
|
||||
rotation_90_z := quaternion128{0, 0, 0.707, 0.707} // 90° around Z
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
## 14. Context System and Defer
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
// Odin has an implicit context system for threading allocators,
|
||||
// loggers, and other utilities through your program
|
||||
|
||||
example_with_context :: proc() {
|
||||
// Save current context
|
||||
old_allocator := context.allocator
|
||||
|
||||
// Use a different allocator temporarily
|
||||
temp_allocator := context.temp_allocator
|
||||
context.allocator = temp_allocator
|
||||
|
||||
// All allocations in this scope use temp_allocator
|
||||
temp_data := make([]int, 100)
|
||||
// No need to delete temp_data - it's automatically cleaned up
|
||||
|
||||
// Restore original allocator
|
||||
context.allocator = old_allocator
|
||||
}
|
||||
|
||||
// defer ensures cleanup happens when scope exits
|
||||
resource_management_example :: proc() {
|
||||
file_handle := os.open("example.txt", os.O_RDONLY, 0) or_return
|
||||
defer os.close(file_handle) // Always closed when function exits
|
||||
|
||||
buffer := make([]u8, 1024)
|
||||
defer delete(buffer) // Always freed when function exits
|
||||
|
||||
// Use file_handle and buffer...
|
||||
// They're automatically cleaned up even if we return early
|
||||
}
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
The [Odin Programming Language website](https://odin-lang.org/) provides
|
||||
excellent documentation and examples.
|
||||
|
||||
The [overview](https://odin-lang.org/docs/overview/) covers much of the
|
||||
language in detail.
|
||||
|
||||
The [GitHub examples repository](https://github.com/odin-lang/examples)
|
||||
contains idiomatic Odin code examples.
|
||||
|
||||
For learning resources:
|
||||
- ["Understanding the Odin Programming Language" by Karl Zylinski](https://odinbook.com)
|
||||
- [Odin Discord](https://discord.gg/sVBPHEv) for community support
|
||||
- [FAQ](https://odin-lang.org/docs/faq/) for common questions
|
Reference in New Issue
Block a user