Control Flow (if, when, switch, for, defer)

If

if x >= 0 {
    fmt.println("x is positive")
}
  • Initial statement :

    • Like for , the if  statement can start with an initial statement to execute before the condition.

    • Variables declared by the initial statement are only  in the scope of that if  statement, including the else  blocks.

    if x := foo(); x < 0 {
        fmt.println("x is negative")
    }
    
    if x := foo(); x < 0 {
        fmt.println("x is negative")
    } else if x == 0 {
        fmt.println("x is zero")
    } else {
        fmt.println("x is positive")
    }
    

If Ternary

bar := 1 if condition else 42
// or
bar := condition ? 1 : 42

For

  • For .

  • It's the only type of loop.

  • Braces { }  or a do  are always required.

for i := 0; i < 10; i++ {
    fmt.println(i);
}
for i := 0; i < 10; i += 1 { }
for i := 0; i < 10; i += 1 do single_statement()
for i in 0..<10 {
    fmt.println(i)
}
// or
for i in 0..=9 {
    fmt.println(i)
}
str: string = "Some text"
for character in str {
    assert(type_of(character) == rune)
    fmt.println(character)
}
  • Doesn't work :

    • My expectation here was that a , b  and c  would be elements from the return of test() . It would be a way to iterate over multiples arrays at the same time.

    test :: proc() -> ([5]f32, [5]int, [5]bool) {
        return {}, {}, {}
    }
    
    for a, b, c in test() {
        fmt.printfln("a: %v, b: %v, c: %v", a, b, c)
    }
    
    for a, b, c in [5]f32{}, [5]int{}, [5]bool{} {
        fmt.printfln("a: %v, b: %v, c: %v", a, b, c)
    }
    
Custom Iterators
  • Loop arguments via the C loop pattern :

    • Only works for 1 loop argument, as far as I know.

    memory_block_found := false
    for block := arena.curr_block; block != nil; block = block.prev {
        if block == temp.block {
            memory_block_found = true
            break
        }
    }
    
  • Loop arguments via the in  pattern :

    • The pattern is somewhat equivalent to for a, b, ok := intersect_next(&iterator_idx, &A, &B); ok { ; this version is specific doesn't work; the in  pattern is a way to make it work. The last return value of the function must be a boolean, used to know whether to exit the loop or not.

    • I believe this works for any number of loop arguments.

    intersect_next :: proc(it: ^u32) -> (f32, [2]f32, bool, matrix[4,2]f32, bool) {
        if it^ == 4 {
            return 0, {}, false, {}, false
        }
        it^ += 1
        @static v: f32
        v += 1
        return v, {}, false, {}, true
    }
    iterator_idx: u32
    for float, vec, flag, mat in intersect_next(&iterator_idx) {
        fmt.printfln("float: %v vec: %v flag: %v matrix: %v", float, vec, flag, mat)
    }
    
    • If removing the final bool :

      • The final type of 4-valued expression must be a boolean, got matrix[4, 2]f32

  • Loop argument via indefinite loop and break :

    • Somewhat equivalent to the pattern above.

    for {
        v := iter(&idx) or_break
    }
    
    idx: u32
    for {
        float, vec, flag, mat := intersect_next(&idx) or_break
        fmt.printfln("float: %v vec: %v flag: %v matrix: %v", float, vec, flag, mat)
    }
    
    for idx: u32; true; {  // `for idx: u32;; {` is not allowed
        float, vec, flag, mat := intersect_next(&idx) or_break
        fmt.printfln("float: %v vec: %v flag: %v matrix: %v", float, vec, flag, mat)
    }
    
  • Loop arguments via out-parameters :

    for intersect_next(&iterator_idx, &_velocities, &_positions, &vel, &pos) {
    }
    
  • Doesn't work :

    iterator_idx: u32
    for a, b, ok := intersect_next(&iterator_idx, &A, &B); ok {
    }
    

Switch

  • switch  is runtime. The compiler doesn't know if those cases are actually reachable or not, so it needs to check them all.

    • The switch evaluates the possibility of entering each case, so the operation inside each case must be compatible.

  • The Switch has no fallthrough, but requires the use of the case  keyword.

switch arch := ODIN_ARCH; arch {
case .i386, .wasm32, .arm32:
    fmt.println("32 bit")
case .amd64, .wasm64p32, .arm64, .riscv64:
    fmt.println("64 bit")
case .Unknown:
    fmt.println("Unknown architecture")
}
Partial
Foo :: enum {
    A,
    B,
    C,
    D,
}

f := Foo.A
switch f {
case .A: fmt.println("A")
case .B: fmt.println("B")
case .C: fmt.println("C")
case .D: fmt.println("D")
case:    fmt.println("?")
}

#partial switch f {
case .A: fmt.println("A")
case .D: fmt.println("D")
}
Type switch
  • v  is the unwrapped value from value .

value: Value = ...
switch v in value {
case string:
    #assert(type_of(v) == string)

case bool:
    #assert(type_of(v) == bool)

case i32, f32:
    // This case allows for multiple types, therefore we cannot know which type to use
    // `v` remains the original union value
    #assert(type_of(v) == Value)
case:
    // Default case
    // In this case, it is `nil`
}
  • Note :

    • Having multiple types in a single case will mean it won't be unwrapped, as there's no one type the complier can guarantee it'll be.

Usage of :
  • Barinzaya:

    • The case value can include {

    a := [2]int {1, 2}
    switch a {
    case {1, 2}:
    fmt.println(a)
    }
    
    • You can add braces after the colon if you want, unadorned blocks are allowed anywhere a statement is.

Defer

  • Defer .

  • A defer statement defers the execution of a statement until the end of the scope it is in.

  • The following will print 4  then 234 .

package main

import "core:fmt"

main :: proc() {
    x := 123
    defer fmt.println(x)
    {
        defer x = 4
        x = 2
    }
    fmt.println(x)

    x = 234
}