Monday, November 28, 2022

The curious case of the extra for loop iteration

 

Lately, I stumbled upon a lovely and elegant bug, yes it's pretty funny to call a bug elegant but my opinion here is quite firm.

Lets, consider the following short piece of code: 

package main

import (
"fmt"
"reflect"
)

type MyStruct struct{}

func (t *MyStruct) Func0() {
fmt.Println("func0")
}

func (t *MyStruct) Func1() {
fmt.Println("func1")
}

func main() {
var t MyStruct
searchTables := [3]uint32{0, 1}
for _, v := range searchTables {
methodName := "Func" + fmt.Sprint(v)
reflect.ValueOf(&t).MethodByName(methodName).
Call([]reflect.Value{})
}
}

Looking through the code we see a range loop running over a two-member array. calling a function by name, nothing really special here unless you see the output, which is :

func0
func1
func0

Wait, What was that? how did a two-member range loop become a three iterations output?

The reason for that lies in the fact the array was declared as a 3-member array, while initializing a static array the zero value of that array (in this case uint32(0)) will fill the last value in the array. 

Admittedly this is not a very hard bug to diagnose, but its a very easy bug to fall into, example scenario: you might have a declared array from which you would like to remove one member, and although that member has been removed you forgot to change the size of the array and the last value will be filled with the zero value of that type which can lead to a disaster in runtime. 

Hope this helps anyone 










 























No comments:

Post a Comment