All posts
I Need To Go: My First Steps in Go
5 min read

I Need To Go: My First Steps in Go

Does Go bring together the best of Python and JavaScript? ๐Ÿค” I recently started the Go Tour and documented some of the syntax quirks that caught me off guard, like the naked returns or the "for-as-while" loop. Here is a look at my first steps learning Go as a JS/Python developer.

I recently decided to start learning Go. To get my hands dirty, I set up the local Go Tour (~/go/bin/tour, as per Go Tour #3) and began working my way through the basics. I've just wrapped up the section on Flow control statements: for, if, else, switch, and defer.

Getting to this point has been an interesting experience, filled with moments of confusion, a few "wow!"s here and there, and some syntax that felt entirely alien at first. Here is a look at my progress so far and the concepts that stood out to me.


1. Some Rather Unusual Syntax

Coming from other languages, the way Go handles functions and returns took some getting used to. Take a look at this multiple-return example from Go Tour:

package main

import "fmt"

func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}

Having only one type declaration for multiple variables, followed by multiple return types in parentheses, felt very weird initially.

But things got even weirder when I hit naked returns:

package main

import "fmt"

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

func main() {
	fmt.Println(split(17))
}

ishowspeed-huh

In Go, you can name your return values at the top of the function. When you do that, a simple return statement without arguments automatically returns whatever those variables currently hold. I don't think I've seen this pattern in other languages before. While it's neat for short functions, I can see how it might make larger functions a bit harder to read if you aren't careful.


2. The Constant Conundrum (and an "Aha!" Moment)

I ran into a bit of a roadblock when trying to understand this example from the tour:

package main

import "fmt"

const (
	// Create a huge number by shifting a 1 bit left 100 places.
	// In other words, the binary number that is 1 followed by 100 zeroes.
	Big = 1 << 100
	// Shift it right again 99 places, so we end up with 1<<1, or 2.
	Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
	return x * 0.1
}

func main() {
	fmt.Println(needInt(Small))
	fmt.Println(needFloat(Small))
	fmt.Println(needFloat(Big))
}

Initially, I got an error when I tried running fmt.Println(needInt(Big)).

I was under the impression that Go just infers types cleanly, so I was confused about why needFloat(Big) worked while needInt(Big) threw a compilation error.

After digging into it, I learned something crucial: untyped constants in Go can be arbitrarily large. They only take on a specific type size when they are used in a context that requires one.

  • On my system, a standard int is 64 bits wide, meaning it cannot hold a number as massive as 1 << 100.
  • float64 works because it represents values using floating-point scientific notation, allowing it to scale up to represent much larger numbers (with potential limits on precision tho).

Once that clicked, the error made complete sense.

agreed-speed


3. Rethinking the Loop

Go keeps things simple by only having one looping construct: for. However, how you use it can vary wildly.

I was quite surprised to find out that the initialization and post statements in a for loop are completely optional:

package main

import "fmt"

func main() {
	sum := 1
	for ; sum < 1000; {
		sum += sum
	}
	fmt.Println(sum)
}

Looking at those empty semicolons felt a bit strange. But then it was explained that you can drop the semicolons entirely:

package main

import "fmt"

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}

Since I have some experience with Python, this felt much more familiar. It turns out Go doesn't have a while keyword because a for loop with only a condition is Go's version of a while loop.

And if you want an infinite loop? You just drop the condition entirely:

package main

func main() {
	for {
	}
}

I dunno why, but it's kinda funny how simple that "forever" loop looks.

hehe


4. Conditionals: If Python and JS Had a Baby

When writing if statements in Go, I couldn't help but feel like the syntax is a hybrid of Python and JavaScript:

package main

import (
	"fmt"
	"math"
)

func sqrt(x float64) string {
	if x < 0 {
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
	fmt.Println(sqrt(2), sqrt(-4))
}

You don't need parentheses around the condition (like Python), but you absolutely must use curly braces (like JavaScript). It felt slightly unusual at first, but it quickly became comfortable to write.

Another feature that really caught my attention was the ability to execute a short statement before the conditional check:

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

Being able to declare a helper variable v right inside the if scope, where it is only available within the conditional block, is so clean, like, wow. It keeps the outer function scope clutter-free.


What's Next?

I am slowly adapting to Go's design philosophy. While some of the syntax choices felt bizarre at first, I am starting to appreciate how the language forces you to write explicit, readable code.

My next step in the tour is to dive deeper into structs, slices, and maps (even pointers) to see how they... to be honest, I dunno what they do. But for now, I need to take a break. HMU tho ๐Ÿ˜‰

All posts /blogs/go