Hello, Go

Station 0.2 10 min Interchange: <?php & echo → package main

In PHP, the smallest program is one line. Open a file, write <?php echo "Hello";, run it, done. PHP starts at the top of the file and works its way down, running whatever it finds.

Go doesn't run "whatever it finds". There's no code at the top level of a Go file at all: no loose statements, no echo floating around. Instead, every Go program has exactly one front door.

The front door: package main and func main

Here's the Go version of hello world:

package main

import "fmt"

func main() {
	fmt.Println("Hello, Go!")
}

Three things are going on:

  • package main: every Go file starts by saying which package it belongs to. A package is simply all the .go files in one directory, so a program can be split across as many files as you like. main is special: it means "this package builds into a program", not a library.
  • import "fmt": Go has no autoloader. If you use a package, you import it, by name, at the top of the file. fmt (say "fumpt" if you want to sound like you've been doing this for years) is the formatting and printing package.
  • func main(): the entry point. When the program starts, Go calls main. When main returns, the program ends. No arguments, no return value.

fmt.Println is roughly echo, except it adds a newline for you, so no more "\n" or PHP_EOL at the end of every line. Pass it several values and it puts a space between each one, which echo never does. The Interchange tab has both side by side.

Laravel corner

Laravel's real front door is public/index.php: it boots the autoloader, builds the container and hands the request to the kernel. In Go, func main is all of that at once. Whatever your app needs, main builds it, explicitly, in plain code you can read top to bottom. No service providers, no magic. You'll grow to love it, eventually.

Functions that take arguments

main takes nothing and returns nothing, but most functions you write will do both. Here's one you might write for a departure board, next to its PHP twin:

func announce(dest string, platform string) string {
	return dest + " from platform " + platform
}
function announce(string $dest, string $platform): string
{
    return "{$dest} from platform {$platform}";
}

The pieces are all there, just shuffled: the type goes after the name, there's no $, and the return type sits after the closing bracket with no colon. When neighbouring parameters share a type you can group them, so func announce(dest, platform string) string means exactly the same thing. You'll see that shorthand everywhere. (Platform is a string because, as any commuter knows, platform 9b exists.)

The compiler has opinions

Go is compiled, and the compiler is strict in ways PHP never was. Three of them catch every PHP dev in their first week.

Unused imports are errors. Import os and don't use it, and the build fails with "os" imported and not used. Same for unused local variables. It's annoying for about a day, and then you notice your files no longer collect junk.

Single quotes are for single characters. In PHP, 'Hello' and "Hello" are both strings. In Go, double quotes (or backticks) make strings, and single quotes make a rune, which is one character. fmt.Println('Hello') won't compile: more than one character in rune literal.

Mind the gap

Your PSR-12 braces will break the build. PSR-12 puts a function's opening brace on its own line. In Go that's a syntax error, not a style choice: Go inserts semicolons at line ends for you, so func main() followed by a newline is treated as a complete (and broken) statement. The opening brace always goes on the same line. The good news: gofmt formats everything else for you, so this is the last brace argument you'll ever have.

Running it

On your machine, a Go project needs a go.mod file first, the way a PHP project needs a composer.json. go mod init example.com/hello creates one (lesson 0.4 explains what that name is for). After that, go run . compiles and runs the program in the current directory. (Lesson 0.3 covers the toolchain properly.)

In this course, the Run button does the same thing in a sandbox, and Submit runs the hidden tests against your code. Stuck? Hints unlock one at a time, and lesson 0.6 explains exactly what happens behind Submit.

Recap

  • A Go program starts at func main in package main. There's no top-level code.
  • Imports are explicit, and unused imports (or variables) are compile errors.
  • fmt.Println prints its arguments separated by spaces, with a newline at the end. Strings use double quotes, never single.
  • Opening braces go on the same line. Always.

1Hello, world

PHPhello.php
<?php
// PHP runs the file top to bottom.
echo "Hello, PHP!\n";
GOhello.go
package main

import "fmt"

// Go starts here, and only here.
func main() {
	fmt.Println("Hello, Go!")
}

2echo vs fmt.Println

PHPecho.php
<?php
echo "Platform ", 4, " to ", "Leeds", "\n";
// Platform 4 to Leeds  (you add the spaces and the newline)

echo 'Single quotes are fine here';
GOecho.go
fmt.Println("Platform", 4, "to", "Leeds")
// Platform 4 to Leeds  (spaces and newline added for you)

fmt.Println('Leeds') // compile error: single quotes are one character (a rune)

Mind the gap

Println adds spaces between values and a newline at the end. fmt.Print adds no newline, which makes it the closer match to echo.

3Where the brace goes

PHPbraces.php
<?php
// PSR-12: function braces on the next line.
function greet()
{
    echo "Hi";
}
GObraces.go
// Go: the brace goes on the same line. Always.
func greet() {
	fmt.Println("Hi")
}

// func greet()
// {   ← syntax error: unexpected semicolon or newline before {

Mind the gap

Go inserts semicolons at line ends, so a brace on the next line splits func greet() from its body. gofmt can't save you here, because it won't format code that doesn't parse.