DEV Community

Cover image for Learning Go as a Ruby Developer — Part 4: What can we do with a single "for"
Shrouk Abozeid
Shrouk Abozeid

Posted on

Learning Go as a Ruby Developer — Part 4: What can we do with a single "for"

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
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Go uses curly braces to define the block:

if age >= 18 {
    fmt.Println("Adult")
} else {
    fmt.Println("Minor")
}
Enter fullscreen mode Exit fullscreen mode

Coming from Ruby, I had to get used to seeing {} everywhere.


Boolean Operators

The familiar operators are there:

if !active {
    // not active
}
Enter fullscreen mode Exit fullscreen mode
if age == 18 {
    // exactly 18
}
Enter fullscreen mode Exit fullscreen mode

And for inequality:

if age != 18 {
    // not 18
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The traditional form looks like this:

for init; condition; post {
    // code
}
Enter fullscreen mode Exit fullscreen mode

For example:

for i := 0; i < 10; i++ {
    fmt.Println(i)
}
Enter fullscreen mode Exit fullscreen mode

There are three parts:

  1. Initialization — runs before the first iteration
  2. Condition — checked before every iteration
  3. 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)
}
Enter fullscreen mode Exit fullscreen mode

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++
}
Enter fullscreen mode Exit fullscreen mode

This behaves like a while loop.

In other words:

for condition {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

is Go's version of:

while condition
  # ...
end
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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++
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

This gives us both the index and the value.

The output would look conceptually like:

0 Alice
1 Bob
2 Charlie
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Or, if I don't need the index:

names.each do |name|
  puts name
end
Enter fullscreen mode Exit fullscreen mode

In Go, range handles both cases.

for index, name := range names {
    fmt.Println(index, name)
}
Enter fullscreen mode Exit fullscreen mode

And if I only care about the value, I can ignore the index:

for _, name := range names {
    fmt.Println(name)
}
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

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)