Syntax#

Go’s grammar is small, C-shaped, and rigidly formatted. Source files start with package and import; statements end at newlines (the lexer inserts implicit semicolons). gofmt is the only formatter; never argues style.

/types`. For operators, see Operators.

package main

import "fmt"

func greet(name string) {
    fmt.Println("hello,", name)
}

func main() {
    greet("operator")
}

Comments#

Line comments start with //; block comments wrap with /* ... */ (do not nest). A comment immediately above a top-level declaration is the doc comment and shows up in go doc and pkg.go.dev.

// Package scan probes TCP and UDP ports.
package scan

// Probe returns the first response from host:port within timeout.
func Probe(host string, port int, timeout time.Duration) (string, error) {
    /* multi-line
       comment */
    return "", nil
}

Identifiers#

Letters, digits, and underscores; cannot start with a digit. Case controls visibility: identifiers starting with an uppercase letter are exported from the package; lowercase identifiers are package-private. Convention is camelCase for locals and unexported names, PascalCase for exported names, UPPER_SNAKE only for constants imported from other ecosystems.

var userCount   int       // unexported
const MaxRetry  = 5       // exported
type HTTPClient struct {} // exported; acronyms stay uppercase

The blank identifier _ discards a value it does not want to bind.

_, err := os.Stat("file")    // we only care about the error

Keywords#

Reserved words.

break case chan const continue default defer else fallthrough
for func go goto if import interface map package range return
select struct switch type var

Predeclared identifiers (true false nil iota, builtin functions make new len cap append copy delete panic recover, types int string bool error any…) are not reserved but the operator does not shadow them.

Literals#

Every primitive has a literal form.

n   := 42                  // int
big := 1_000_000           // underscore separators (Go 1.13+)
x   := 0xff                // hex
o   := 0o755               // octal (Go 1.13+ form; older 0755 still works)
b   := 0b1010_0001         // binary
pi  := 3.14159             // float64
e   := 6.022e23            // scientific

s   := "hello"             // string (interpreted)
r   := `no \n escapes`     // raw string (backticks); spans lines

c   := 'A'                 // rune (int32)
t   := true
z   := nil                 // valid only for pointers, interfaces, slices,
                           //   maps, channels, and functions

Statements#

A statement does something. Assignment, import, return, defer, go, if / for / switch / select blocks, function calls.

x := 1 + 2                   // short variable declaration
import "fmt"                 // import declaration
if x > 0 {
    fmt.Println("positive")
}
for i := 0; i < 3; i++ {
    fmt.Println(i)
}
return x

Semicolons separate statements; the lexer inserts them at newlines following an identifier, literal, ), ], }, or specific keywords. Never writes them explicitly.

Expressions#

An expression evaluates to a value. Arithmetic, function calls, type conversions, composite literals, and channel receives are all expressions.

1 + 2                        // arithmetic
len(xs) > 0                  // relational
fmt.Sprintf("%d", n)         // function call
[]int{1, 2, 3}               // composite literal
<-ch                         // channel receive

Go has no ternary; write a four-line if or a helper function.

Blocks#

A block is a { ... } braced group. Every if, for, switch, select, and func body is a block. The opening brace must be on the same line as the keyword (the semicolon-insertion rule would otherwise terminate the statement).

if x > 0 {       // OK
    // ...
}

if x > 0
{                // syntax error
    // ...
}

References#