So loops in ruby has several ways to implement
10.times do |i|
puts i
end
while condition
# ...
end
users.each do |user|
# ...
end
until finished
# ...
end
Go looks at all of this and basically says:
We only need one loop.
That loop is for.
Before getting into loops, though, let's look at conditions.
Conditions in Go
Go has the familiar if, else if, and else.
if age >= 18 {
fmt.Println("Adult")
} else {
fmt.Println("Minor")
}
The syntax is pretty close to Ruby, but there is one important difference.
Go does not use end.
Ruby:
if age >= 18
puts "Adult"
else
puts "Minor"
end
Go uses curly braces to define the block:
if age >= 18 {
fmt.Println("Adult")
} else {
fmt.Println("Minor")
}
Coming from Ruby, I had to get used to seeing {} everywhere.
Boolean Operators
The familiar operators are there:
if !active {
// not active
}
if age == 18 {
// exactly 18
}
And for inequality:
if age != 18 {
// not 18
}
So the basic logic isn't particularly difficult to transition to.
It's mostly the syntax that's different.
now going back to loops
The for Loop
Go has only one looping construct:
for
The traditional form looks like this:
for init; condition; post {
// code
}
For example:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
There are three parts:
- Initialization — runs before the first iteration
- Condition — checked before every iteration
- Post statement — runs after every iteration
This is very similar to the traditional C-style loop.
And this is where my C++ nostalgia started showing up.
Go doesn't have separate times, while, or until loops.
Instead, you use for.
for i := 0; i < 10; i++ {
fmt.Println(i)
}
One keyword, several forms.
The While-Like for
What if we don't need the initialization and post statements?
No problem.
x := 1
for x < 10 {
fmt.Println(x)
x++
}
This behaves like a while loop.
In other words:
for condition {
// ...
}
is Go's version of:
while condition
# ...
end
This is one of those examples where Go's simplicity starts making more sense.
Instead of introducing another keyword for while, Go reuses for.
Infinite Loops
You can even remove the condition completely:
for {
// keep going
}
This creates an infinite loop.
Of course, you'll usually want some way to escape it.
That's where break comes in.
x := 1
for {
if x > 9 {
break
}
fmt.Println(x)
x++
}
The loop keeps running until the break statement is reached.
Ruby has a similar concept:
x = 1
loop do
break if x > 9
puts x
x += 1
end
Again, Go doesn't need a separate loop construct.
for handles it.
break and continue
Go also provides the familiar break and continue keywords.
break
break exits the loop completely.
for i := 0; i < 10; i++ {
if i == 5 {
break
}
fmt.Println(i)
}
The loop stops when i reaches 5.
continue
continue skips the remaining code in the current iteration and moves to the next one.
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i)
}
This skips even numbers and prints the odd ones.
Ruby has the same concepts:
10.times do |i|
next if i.even?
puts i
end
Ruby uses next where Go uses continue.
range: The Go Way to Iterate Over Collections
The for loop becomes even more interesting when combined with range.
The range form allows you to iterate over collections such as:
- arrays
- slices
- strings
- maps
- channels
For example:
names := []string{"Alice", "Bob", "Charlie"}
for index, name := range names {
fmt.Println(index, name)
}
This gives us both the index and the value.
The output would look conceptually like:
0 Alice
1 Bob
2 Charlie
Comparing range with Ruby
In Ruby, I'd probably write:
names = ["Alice", "Bob", "Charlie"]
names.each_with_index do |name, index|
puts "#{index} #{name}"
end
Or, if I don't need the index:
names.each do |name|
puts name
end
In Go, range handles both cases.
for index, name := range names {
fmt.Println(index, name)
}
And if I only care about the value, I can ignore the index:
for _, name := range names {
fmt.Println(name)
}
The _ tells Go that I intentionally don't need that value.
Switch Statements
Go also has switch for conditional logic.
A simple switch looks like this:
switch name {
case "Moneypenny":
fmt.Println("Miss Moneypenny")
case "Bond":
fmt.Println("Bond, James Bond")
case "Q":
fmt.Println("This is Q")
default:
fmt.Println("Unknown")
}
Unlike Ruby's case, Go doesn't require an end.
Ruby:
case name
when "Moneypenny"
puts "Miss Moneypenny"
when "Bond"
puts "Bond, James Bond"
when "Q"
puts "This is Q"
else
puts "Unknown"
end
But there is another interesting form of Go's switch.
Switch Without an Expression
You can leave the value out completely:
switch {
case age < 18:
fmt.Println("Minor")
case age >= 18:
fmt.Println("Adult")
}
Each case is essentially a condition.
This can be useful when you have several related conditions.
Ruby's case can also be used for conditional expressions, although the syntax and matching behavior are different.
Multiple Values in a Case
A Go case can match multiple values:
switch name {
case "Moneypenny", "Bond", "Dr No":
fmt.Println("Secret agent")
default:
fmt.Println("Unknown")
}
This means we don't need separate cases for every value when they should produce the same result.
fallthrough
One thing that caught my attention was fallthrough.
Normally, once a Go switch finds a matching case, it stops.
switch {
case true:
fmt.Println("first")
case true:
fmt.Println("second")
}
Only the first case runs.
If you explicitly use fallthrough:
switch {
case true:
fmt.Println("first")
fallthrough
case true:
fmt.Println("second")
case true:
fmt.Println("third")
}
The first and second cases run, but the third does not.
This is different from the behavior many people might expect from C-style switch statements, where falling through can happen unless you explicitly stop it.
Go makes the behavior explicit with fallthrough.
Go vs Ruby: Control Flow
Here's how the two languages compare.
| Concept | Go | Ruby |
|---|---|---|
| Condition |
if / else
|
if / else
|
| Not | ! |
! |
| Equality | == |
== |
| Inequality | != |
!= |
| Main loop | for |
Multiple constructs |
| While loop | for condition |
while condition |
| Infinite loop | for {} |
loop do |
| Iterate collection | for ... range |
.each |
| Skip iteration | continue |
next |
| Exit loop | break |
break |
| Switch | switch |
case |
| Multiple switch values | case "a", "b" |
when "a", "b" |
Neither approach is necessarily better.
They're just optimizing for different things.
Top comments (0)