Table of contents
var and :=
There are two ways to declare variables in Go: using the var keyword or the short declaration operator :=.
package main
import "fmt"
func main() {
var name string = "Go"
age := 15 // Born in 2009, after all
fmt.Println(name, age)
}
var can be used anywhere — inside or outside functions. On the other hand, := is shorthand syntax that can only be used inside functions. You don’t need to specify the type; it’s automatically inferred from the value on the right.
So when should you use which? When assigning a value immediately inside a function, := is much cleaner. var is for package-level variables or when you need to declare a variable without an initial value.
package main
import "fmt"
var globalMsg = "package-level variable" // Cannot use :=
func main() {
var count int // Declared without initial value — initialized to 0
name := "gopher" // More concise inside functions
fmt.Println(globalMsg, count, name)
}
You can also declare multiple variables at once.
var (
host = "localhost"
port = 8080
debug = false
)
This block declaration is useful for grouping related variables at the package level. It’s a great pattern for organizing configuration values or constant groups.
Constants: const
Use const for values that don’t change. Since constants must be determined at compile time, you can’t assign function call results to them.
package main
import "fmt"
const pi = 3.14159
const greeting = "Hello, Go!"
const (
statusOK = 200
statusNotFound = 404
)
func main() {
fmt.Println(pi, greeting)
fmt.Println(statusOK, statusNotFound)
}
Go constants have an interesting characteristic: the concept of untyped constants.
package main
import "fmt"
const x = 100 // Untyped — not int, not float64, just a 'number'
func main() {
var i int = x // Used as int
var f float64 = x // Used as float64 too
fmt.Println(i, f) // 100 100
}
Untyped constants have their type determined by the context in which they’re used. This means the same constant 100 can be assigned to both int and float64. This is a unique Go design choice that allows flexible use of constants.
iota is also worth knowing. It’s Go’s tool for creating something similar to enumerations.
package main
import "fmt"
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Println(Wednesday) // 3
}
iota starts at 0 within a const block and increments by 1 for each line. It serves a similar role to Java’s enum or consecutive C #define declarations, but it’s much more concise.
Basic Types
Go’s type system is concise. It has what you need and nothing more.
| Category | Type | Description |
|---|---|---|
| Integer | int, int8, int16, int32, int64 | int is 32-bit or 64-bit depending on the platform |
| Unsigned integer | uint, uint8, uint16, uint32, uint64 | Positive numbers only |
| Float | float32, float64 | Use float64 as the default for decimals |
| Complex | complex64, complex128 | For scientific computation |
| String | string | UTF-8, immutable |
| Boolean | bool | true / false |
| Byte | byte | Alias for uint8 |
| Rune | rune | Alias for int32, represents a Unicode code point |
In most cases, four types are enough: int, float64, string, and bool. Unless you’re working in an embedded environment where memory matters, you rarely need to specify exact bit sizes.
package main
import "fmt"
func main() {
var age int = 25
var height float64 = 175.5
var name string = "gopher"
var active bool = true
fmt.Println(age, height, name, active)
}
The distinction between byte and rune becomes important when dealing with characters. byte represents a single ASCII character, while rune represents a single Unicode character. When working with multibyte characters like Korean, you need to use rune.
package main
import "fmt"
func main() {
s := "Go는 재밌다"
fmt.Println(len(s)) // 15 — byte count
fmt.Println(len([]rune(s))) // 7 — character count
}
When you pass a string to len(), it returns the byte count. Since each Korean character is 3 bytes, the numbers differ. If you want the actual character count, convert to []rune first and then check the length.
Type Conversion
Go does not allow implicit type conversion. This reflects Go’s philosophy. In C or Java, assigning an int to a float64 converts it automatically, but Go requires explicit conversion.
package main
import "fmt"
func main() {
i := 42
f := float64(i) // int -> float64
u := uint(f) // float64 -> uint
fmt.Println(i, f, u) // 42 42 42
}
This might feel tedious at first. But considering the subtle bugs that implicit conversion can introduce, it’s actually the safer choice. It especially prevents unintended precision loss when mixing integers and floating-point numbers.
For conversion between strings and numbers, use the strconv package.
package main
import (
"fmt"
"strconv"
)
func main() {
// Number -> string
s := strconv.Itoa(42)
fmt.Println(s) // "42"
// String -> number
n, err := strconv.Atoi("123")
if err != nil {
fmt.Println("conversion failed:", err)
return
}
fmt.Println(n) // 123
}
Notice how Atoi returns both a value and an error. In Go, operations that can fail routinely return an error as the second return value. This will be covered in detail in Part 4 on error handling.
Zero Value
In Go, when you declare a variable without an initial value, it’s automatically assigned the zero value for its type.
package main
import "fmt"
func main() {
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // "" (empty string)
fmt.Println(i, f, b, s)
}
| Type | Zero Value |
|---|---|
| Integer / Float | 0 |
| bool | false |
| string | "" |
| Pointer, slice, map, channel, function, interface | nil |
This isn’t just a convenience feature — it’s a Go design principle. It eliminates the concept of “uninitialized variables” entirely. If you’ve ever struggled debugging garbage values from uninitialized variables in C, you can appreciate how valuable this decision is.
This property is actively leveraged in practice. For example, an int counter starts at 0 just by declaring it, and a bool flag defaults to false. No separate initialization code is needed, keeping the code clean.
The next part covers Go’s conditionals and loops. We’ll look at the unique syntax of adding an initializer to if, and why Go doesn’t have while.




Loading comments...