Variables and Constants
Go is explicit and predictable: you declare names (variables/constants), give them types (sometimes inferred), and assign values. Choosing between var, :=, and const is mostly about scope, clarity, and whether the value must never change.
var: Declare a Variable (Optionally With a Type)
Use var when you want an explicit type, when you need a package-level variable, or when you want to declare first and assign later.
package main
import "fmt"
func main() {
var count int
fmt.Println(count) // zero value: 0
count = 3
fmt.Println(count)
var name = "gopher" // type inferred as string
fmt.Println(name)
}Zero Values (Default Values)
If you declare a variable without assigning a value, Go assigns a zero value based on the type. This makes code safer because you never read “uninitialized memory.”
int,float64:0,0.0bool:falsestring:""(empty string)rune(alias ofint32):0
Short Declaration :=: Declare + Assign Inside Functions
Inside a function, := is the most common way to create variables. It declares the variable and infers the type from the right-hand side.
func main() {
count := 10 // int
rate := 2.5 // float64
active := true // bool
label := "demo" // string
_ = count
_ = rate
_ = active
_ = label
}Rule of thumb: use := for local variables when the inferred type is obvious; use var when you want the type to stand out or you need a zero value first.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
const: Compile-Time Constants
Use const for values that must not change and are known at compile time (numbers, strings, booleans). Constants improve readability and prevent accidental reassignment.
const maxRetries = 3
const appName = "Counter"
func main() {
// maxRetries = 4 // compile error: cannot assign to const
}When to choose each:
const: value must never change; configuration-like literals; sizes; fixed thresholds.:=: most local variables; quick, clear initialization.var: you need a specific type, a zero value first, or a variable outside a function.
Basic Types and Explicit Conversions
Go has a small set of basic types that you’ll use constantly. The key rule: Go does not do implicit numeric conversions; you must convert explicitly.
Integers and Floats
Common integer types include int (machine word size) and fixed-size types like int64. For floating point, float64 is the usual default.
var a int = 5
var b int64 = 10
var f float64 = 2.75
_ = a
_ = b
_ = fStrings
Strings are immutable sequences of bytes. You can concatenate with + and get length with len (bytes, not characters).
s := "go"
t := s + "lang"
size := len(t)
_ = sizeBooleans
bool values are true or false. Conditions in if and for must be boolean expressions (no “truthy” integers).
ready := true
if ready {
// ...
}Runes (Characters)
A rune represents a Unicode code point and is an alias for int32. Single quotes produce a rune literal.
r := 'A' // rune
code := int32(r) // explicit conversion not required here, but shown for clarity
_ = codeExplicit Type Conversions Only
If you mix numeric types, convert explicitly. This is intentional: it prevents subtle bugs.
var x int = 3
var y int64 = 4
sum := int64(x) + y
var n int = int(2.9) // truncates toward zero: 2
_ = sum
_ = nConversions between string and numeric types are not done with simple casts. For example, converting "123" to an int requires parsing (covered later when you use standard library helpers).
Control Flow
Go keeps control flow small and consistent: if, for, switch, and a few keywords like defer for cleanup.
if (Including Short Statements)
An if can include a short statement before the condition. The variable declared there is scoped to the if/else block.
func isPositive(n int) bool {
if n > 0 {
return true
}
return false
}func clampToMax(n, max int) int {
if v := n; v > max {
return max
}
return n
}Step-by-step for the short statement form:
- Run the short statement (
v := n). - Evaluate the condition (
v > max). - Execute the matching branch;
vexists only inside thatif/else.
for: Go’s Only Loop
Go uses for for all looping patterns: counting loops, while-style loops, and infinite loops. Use break and continue to control iteration.
func sumFirstN(n int) int {
sum := 0
for i := 1; i <= n; i++ {
sum += i
}
return sum
}func countdown(from int) {
for from > 0 { // while-style
from--
}
}func waitUntilReady() {
for { // infinite loop
ready := true
if ready {
break
}
}
}switch: Clear Multi-Branch Logic
switch compares cases top-to-bottom and runs the first matching case. Unlike some languages, Go does not fall through by default.
func grade(score int) string {
switch {
case score >= 90:
return "A"
case score >= 80:
return "B"
case score >= 70:
return "C"
default:
return "F"
}
}If you explicitly want fallthrough behavior, you must write fallthrough. It jumps to the next case body (it does not re-check the next case condition).
func describeDay(n int) string {
switch n {
case 6:
return "Saturday"
case 7:
return "Sunday"
default:
return "Weekday"
}
}func demoFallthrough(x int) int {
switch x {
case 1:
x += 10
fallthrough
case 2:
x += 100
default:
x += 1000
}
return x
}defer: Schedule Cleanup
defer schedules a function call to run when the surrounding function returns (whether it returns normally or early). This is commonly used for cleanup such as closing files or unlocking a mutex.
func doWork() int {
resource := "acquired"
_ = resource
defer func() {
// cleanup runs at function return
resource = "released"
}()
return 42
}Practical rule: place defer immediately after acquiring something that must be released, so the cleanup is hard to forget.
Functions
Functions are the main unit of organization in Go. They can take parameters, return values (including multiple values), and optionally name return values.
Parameters and Return Values
func add(a int, b int) int {
return a + b
}If adjacent parameters share a type, you can shorten the signature:
func add(a, b int) int {
return a + b
}Named Returns
You can name return values in the function signature. This can improve clarity for complex functions, but avoid overusing it. A named return variable starts with its zero value and can be returned with a bare return.
func splitSum(a, b int) (sum int, diff int) {
sum = a + b
diff = a - b
return
}Multiple Return Values
Multiple returns are used heavily in Go, especially for “value + ok” and “value + error” patterns.
func divMod(a, b int) (int, int) {
q := a / b
r := a % b
return q, r
}You can ignore a return value using the blank identifier _:
q, _ := divMod(10, 3)
_ = qResult + Error: The Common Go Pattern
Instead of exceptions, Go typically returns an error as the second value. If the error is non-nil, the first value is often a zero value (or otherwise invalid) and should not be used.
type simpleError string
func (e simpleError) Error() string { return string(e) }
func parsePort(s string) (int, error) {
if len(s) == 0 {
return 0, simpleError("port is required")
}
port := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
return 0, simpleError("port must contain only digits")
}
port = port*10 + int(c-'0')
if port > 65535 {
return 0, simpleError("port out of range")
}
}
if port == 0 {
return 0, simpleError("port must be between 1 and 65535")
}
return port, nil
}Step-by-step, this function behaves like a small CLI validator:
- Reject empty input early.
- Loop through each byte and ensure it is a digit.
- Build the integer value incrementally.
- Validate the numeric range.
- Return
(value, nil)on success, or(0, error)on failure.
func main() {
port, err := parsePort("8080")
if err != nil {
// handle error
return
}
_ = port
}