Secrets in Range and Index Iteration in Go

Do you use index or range to iterate an array or slice in Go?
Have you ever thought about their difference?
Why there is a range if there is a index iteration already?
Let’s have a quick easy problem in 20. Valid Parentheses
We know that sis a string, if you define:
cache := map[byte]byte{
'(': ')',
'{': '}',
'[': ']',
}
stack := []byte{}Can you do below?
for _, sub := range s {
if sub == '(' || sub == '{' || sub == '[' {
stack = append(stack, sub)
}
No!
In Go, a string is a sequence of bytes, but when you iterate over a string using a range loop, the type of each character is rune, not byte. A rune in Go is an alias for int32 and represents a Unicode code point, which means it can handle any character in the Unicode standard, not just ASCII characters.
So we have to use:
cache := map[rune]rune{
'(': ')',
'{': '}',
'[': ']',
}
stack := []rune{}If we use index iteration, we can define cache and stack as byte slice:
cache := map[byte]byte{
'(': ')',
'{': '}',
'[': ']',
}
stack := []byte{}
for i := 0; i < len(s); i++ {
sub := s[i]
}Speed Also Matters
Let’s see another case and run some benchmarking tests:
package test
import (
"testing"
)
func BenchmarkRange(b *testing.B) {
arr := make([]int, 100000)
for i := 0; i < b.N; i++ {
sum := 0
for _, v := range arr {
sum += v
}
}
}
func BenchmarkIndex(b *testing.B) {
arr := make([]int, 100000)
for i := 0; i < b.N; i++ {
sum := 0
for j := 0; j < len(arr); j++ {
sum += arr[j]
}
}
}As to for _, v := range arr and for j := 0; j < len(arr); j++ , which is faster?
go test -bench=.
goos: darwin
goarch: arm64
pkg: tictactoe
BenchmarkRange-8 39072 29106 ns/op
BenchmarkIndex-8 41446 28905 ns/op
PASS
ok tictactoe 3.134sThe benchmark results show that the time taken per operation for both the BenchmarkRange and BenchmarkIndex functions is quite similar, with BenchmarkIndex being slightly faster.
Here’s a breakdown of the results:
BenchmarkRange-8: The-8indicates that the benchmark was run with a GOMAXPROCS value of 8. The function was called 39072 times, and each operation took about 29106 nanoseconds on average.BenchmarkIndex-8: Similarly, this function was called 41446 times, and each operation took about 28905 nanoseconds on average.
These results suggest that using an index to iterate over the array is slightly faster than using range in this case.
Why?
In Go, using an index to iterate over an array or slice can be faster than using range, especially for larger arrays or slices. This is because range creates a copy of the element it's ranging over, which can add overhead.
If performance is a concern and you don’t need a copy of the elements you’re iterating over, it can be faster to use an index to iterate over the elements directly.
In some cases, the convenience and readability of range might outweigh the small performance gain from using an index.
Some cases you might want to be faster:
For solving this leetcode problem: 794. Valid Tic-Tac-Toe State
Let’s see the difference between using range (left) and index (right):


If beats 100% matters to you, then you might want to choose the faster method. Think about a game leaderboard, you don’t want to just beat 60% of your competitors! You want to be on the top!






