Skip to main content

Go Data Types

Introduction

Data types are a fundamental concept in any programming language. They define what kind of data a variable can hold and how the computer should interpret that data. Go, also known as Golang, is a statically-typed language, which means that variable types are checked at compile time, providing both safety and performance benefits.

In this tutorial, we'll explore Go's various data types, understand their characteristics, and learn how to use them in our programs. Whether you're storing simple numbers, text, or building complex data structures, understanding Go's type system is essential for writing effective Go code.

Basic Types in Go

Go provides several built-in basic types that serve as the building blocks for more complex data structures.

Numeric Types

Go offers various numeric types that can be categorized into integers and floating-point numbers.

Integer Types

Integers are whole numbers without a decimal component. Go provides both signed (can be positive or negative) and unsigned (only positive) integer types.

TypeDescriptionSizeRange
int88-bit signed integer1 byte-128 to 127
int1616-bit signed integer2 bytes-32,768 to 32,767
int3232-bit signed integer4 bytes-2,147,483,648 to 2,147,483,647
int6464-bit signed integer8 bytes-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
uint88-bit unsigned integer1 byte0 to 255
uint1616-bit unsigned integer2 bytes0 to 65,535
uint3232-bit unsigned integer4 bytes0 to 4,294,967,295
uint6464-bit unsigned integer8 bytes0 to 18,446,744,073,709,551,615
intPlatform-dependent (32 or 64 bits)4 or 8 bytesDepends on platform
uintPlatform-dependent (32 or 64 bits)4 or 8 bytesDepends on platform
uintptrUnsigned integer for storing pointer valuesPlatform-dependentDepends on platform

Here's a simple example of using integer types:

go
package main

import "fmt"

func main() {
var age int = 25 // Platform-dependent integer
var distance int64 = 54000000 // 64-bit integer for larger values
var small uint8 = 255 // 8-bit unsigned integer (0-255)

fmt.Println("Age:", age)
fmt.Println("Distance:", distance, "km")
fmt.Println("Small value:", small)
}

Output:

Age: 25
Distance: 54000000 km
Small value: 255

Floating-Point Types

Floating-point types can represent decimal values in Go.

TypeDescriptionSizePrecision
float3232-bit floating-point4 bytes~7 decimal digits
float6464-bit floating-point8 bytes~15 decimal digits
go
package main

import "fmt"

func main() {
var pi float64 = 3.14159265359
var temperature float32 = 28.5

fmt.Println("Pi (float64):", pi)
fmt.Println("Temperature (float32):", temperature)

// Notice precision differences
var precise float64 = 0.1234567890123456789
var lessPrecise float32 = 0.1234567890123456789

fmt.Println("Float64 precision:", precise)
fmt.Println("Float32 precision:", lessPrecise)
}

Output:

Pi (float64): 3.14159265359
Temperature (float32): 28.5
Float64 precision: 0.12345678901234568
Float32 precision: 0.12345679

Note that float32 has less precision than float64. For most applications, it's recommended to use float64 for floating-point calculations.

Complex Types

Go also has built-in support for complex numbers.

TypeDescriptionSize
complex64Complex numbers with float32 real and imaginary parts8 bytes
complex128Complex numbers with float64 real and imaginary parts16 bytes
go
package main

import "fmt"

func main() {
var c1 complex64 = 3 + 4i // complex64 (float32 + float32i)
var c2 complex128 = 5 + 7i // complex128 (float64 + float64i)

fmt.Println("Complex64:", c1)
fmt.Println("Complex128:", c2)

// Operations with complex numbers
sum := c1 + complex64(c2)
fmt.Println("Sum:", sum)

// Accessing real and imaginary parts
fmt.Println("Real part of c1:", real(c1))
fmt.Println("Imaginary part of c1:", imag(c1))
}

Output:

Complex64: (3+4i)
Complex128: (5+7i)
Sum: (8+11i)
Real part of c1: 3
Imaginary part of c1: 4

Boolean Type

The boolean type bool represents logical values: either true or false.

go
package main

import "fmt"

func main() {
var isActive bool = true
var isCompleted bool = false

fmt.Println("Is active:", isActive)
fmt.Println("Is completed:", isCompleted)

// Boolean operations
and := isActive && isCompleted // Logical AND
or := isActive || isCompleted // Logical OR
not := !isActive // Logical NOT

fmt.Println("AND:", and)
fmt.Println("OR:", or)
fmt.Println("NOT:", not)
}

Output:

Is active: true
Is completed: false
AND: false
OR: true
NOT: false

String Type

Strings in Go are immutable sequences of bytes, commonly used to represent text. Go strings are encoded as UTF-8 by default.

go
package main

import "fmt"

func main() {
var greeting string = "Hello, Go!"
var multiline string = `This is a
multiline
string.`

fmt.Println(greeting)
fmt.Println(multiline)

// String operations
fmt.Println("Length:", len(greeting))
fmt.Println("First character:", string(greeting[0]))
fmt.Println("Substring:", greeting[7:10]) // Slicing (from index 7 to 9)

// Concatenation
fullGreeting := greeting + " Welcome to programming."
fmt.Println(fullGreeting)
}

Output:

Hello, Go!
This is a
multiline
string.
Length: 10
First character: H
Substring: Go!
Hello, Go! Welcome to programming.

Byte and Rune Types

In Go, a byte is an alias for uint8 and is used to represent a single 8-bit ASCII character. A rune is an alias for int32 and is used to represent a Unicode code point (a single Unicode character).

go
package main

import "fmt"

func main() {
var firstLetter byte = 'A' // ASCII character
var emoji rune = '😀' // Unicode character

fmt.Printf("Byte value: %v, Character: %c
", firstLetter, firstLetter)
fmt.Printf("Rune value: %v, Character: %c
", emoji, emoji)

// Iterating over string characters (runes)
message := "Hello, 世界"
for i, char := range message {
fmt.Printf("Index: %d, Rune: %c, Unicode: %U
", i, char, char)
}
}

Output:

Byte value: 65, Character: A
Rune value: 128512, Character: 😀
Index: 0, Rune: H, Unicode: U+0048
Index: 1, Rune: e, Unicode: U+0065
Index: 2, Rune: l, Unicode: U+006C
Index: 3, Rune: l, Unicode: U+006C
Index: 4, Rune: o, Unicode: U+006F
Index: 5, Rune: ,, Unicode: U+002C
Index: 6, Rune: , Unicode: U+0020
Index: 7, Rune: 世, Unicode: U+4E16
Index: 10, Rune: 界, Unicode: U+754C

Notice that non-ASCII characters like "世" and "界" take multiple bytes in UTF-8 encoding, which is why the indices jump from 7 to 10.

Composite Types in Go

Composite types are types constructed from other types. Go provides several built-in composite types.

Arrays

An array is a fixed-size sequence of elements of the same type.

go
package main

import "fmt"

func main() {
// Declare and initialize an array
var fruits [3]string
fruits[0] = "Apple"
fruits[1] = "Banana"
fruits[2] = "Cherry"

fmt.Println("Fruits array:", fruits)

// Short declaration with initialization
colors := [4]string{"Red", "Green", "Blue", "Yellow"}
fmt.Println("Colors array:", colors)

// Using ... for automatic size determination
numbers := [...]int{1, 2, 3, 4, 5}
fmt.Println("Numbers array:", numbers)
fmt.Println("Length of numbers array:", len(numbers))

// Multidimensional arrays
matrix := [2][3]int{
{1, 2, 3},
{4, 5, 6},
}
fmt.Println("Matrix:", matrix)
}

Output:

Fruits array: [Apple Banana Cherry]
Colors array: [Red Green Blue Yellow]
Numbers array: [1 2 3 4 5]
Length of numbers array: 5
Matrix: [[1 2 3] [4 5 6]]

Slices

Slices are dynamic, flexible views into arrays. Unlike arrays, slices can grow and shrink.

go
package main

import "fmt"

func main() {
// Creating a slice with make
scores := make([]int, 3) // Creates a slice with length 3 and capacity 3
scores[0] = 95
scores[1] = 89
scores[2] = 78
fmt.Println("Scores:", scores)

// Creating a slice from an array
numbers := [5]int{1, 2, 3, 4, 5}
slice1 := numbers[1:4] // Elements from index 1 to 3
fmt.Println("Slice from array:", slice1)

// Creating a slice directly
cities := []string{"New York", "London", "Tokyo", "Paris"}
fmt.Println("Cities:", cities)

// Append to a slice
cities = append(cities, "Sydney")
fmt.Println("After append:", cities)

// Length and capacity
fmt.Println("Length:", len(cities))
fmt.Println("Capacity:", cap(cities))

// Slicing a slice
europeanCities := cities[1:3]
fmt.Println("European cities:", europeanCities)

// Changing a slice affects the underlying array
europeanCities[0] = "Berlin"
fmt.Println("After modification:")
fmt.Println("Cities:", cities)
fmt.Println("European cities:", europeanCities)
}

Output:

Scores: [95 89 78]
Slice from array: [2 3 4]
Cities: [New York London Tokyo Paris]
After append: [New York London Tokyo Paris Sydney]
Length: 5
Capacity: 8
European cities: [London Tokyo]
After modification:
Cities: [New York Berlin Tokyo Paris Sydney]
European cities: [Berlin Tokyo]

Maps

Maps are unordered collections of key-value pairs, similar to dictionaries or hash tables in other languages.

go
package main

import "fmt"

func main() {
// Declare and initialize a map
var studentScores map[string]int = map[string]int{
"Alice": 92,
"Bob": 85,
"Carol": 97,
}
fmt.Println("Student scores:", studentScores)

// Using make to create a map
phoneBook := make(map[string]string)
phoneBook["John"] = "555-1234"
phoneBook["Mary"] = "555-5678"
phoneBook["Tom"] = "555-9012"
fmt.Println("Phone book:", phoneBook)

// Accessing map values
fmt.Println("Bob's score:", studentScores["Bob"])

// Checking if a key exists
score, exists := studentScores["David"]
if exists {
fmt.Println("David's score:", score)
} else {
fmt.Println("David is not in the student list")
}

// Delete a key-value pair
delete(phoneBook, "Tom")
fmt.Println("After deletion:", phoneBook)

// Length of a map
fmt.Println("Number of students:", len(studentScores))
}

Output:

Student scores: map[Alice:92 Bob:85 Carol:97]
Phone book: map[John:555-1234 Mary:555-5678 Tom:555-9012]
Bob's score: 85
David is not in the student list
After deletion: map[John:555-1234 Mary:555-5678]
Number of students: 3

Structs

Structs are composite types that group together variables under a single name.

go
package main

import "fmt"

// Define a struct type
type Person struct {
FirstName string
LastName string
Age int
Email string
}

func main() {
// Create a struct
var p1 Person
p1.FirstName = "John"
p1.LastName = "Doe"
p1.Age = 30
p1.Email = "[email protected]"

fmt.Println("Person 1:", p1)

// Short declaration with initialization
p2 := Person{
FirstName: "Jane",
LastName: "Smith",
Age: 28,
Email: "[email protected]",
}
fmt.Println("Person 2:", p2)

// Omitting field names (not recommended - order dependent)
p3 := Person{"Robert", "Johnson", 35, "[email protected]"}
fmt.Println("Person 3:", p3)

// Accessing struct fields
fmt.Println("Person 2's name:", p2.FirstName, p2.LastName)

// Nested structs
type Address struct {
Street string
City string
Country string
Zipcode string
}

type Employee struct {
Person // Embedded struct
Address Address
Salary float64
JobTitle string
}

emp := Employee{
Person: Person{
FirstName: "Alice",
LastName: "Brown",
Age: 32,
Email: "[email protected]",
},
Address: Address{
Street: "123 Main St",
City: "Boston",
Country: "USA",
Zipcode: "02108",
},
Salary: 75000.0,
JobTitle: "Software Engineer",
}

fmt.Println("Employee:", emp)
fmt.Println("Employee name:", emp.FirstName, emp.LastName) // Accessing embedded struct fields
fmt.Println("Employee city:", emp.Address.City)
}

Output:

Person 1: {John Doe 30 [email protected]}
Person 2: {Jane Smith 28 [email protected]}
Person 3: {Robert Johnson 35 [email protected]}
Person 2's name: Jane Smith
Employee: {{Alice Brown 32 [email protected]} {123 Main St Boston USA 02108} 75000 Software Engineer}
Employee name: Alice Brown
Employee city: Boston

Type Conversions in Go

Go is a statically typed language with strong type checking. Type conversions must be explicit in Go, meaning you have to intentionally convert one type to another.

go
package main

import (
"fmt"
"strconv"
)

func main() {
// Numeric type conversions
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

fmt.Println("int:", i)
fmt.Println("float64:", f)
fmt.Println("uint:", u)

// Converting between strings and numbers
var s string = strconv.Itoa(i) // Int to string
fmt.Println("String from int:", s)

n, err := strconv.Atoi("42") // String to int
if err == nil {
fmt.Println("Int from string:", n)
}

f2, err := strconv.ParseFloat("3.14", 64) // String to float64
if err == nil {
fmt.Println("Float from string:", f2)
}

b, err := strconv.ParseBool("true") // String to bool
if err == nil {
fmt.Println("Bool from string:", b)
}

// Converting to and from string
s2 := fmt.Sprintf("%d", 123) // Using fmt package for conversion
fmt.Println("String from Sprintf:", s2)

var r rune = 'A'
s3 := string(r) // Rune to string
fmt.Println("String from rune:", s3)
}

Output:

int: 42
float64: 42
uint: 42
String from int: 42
Int from string: 42
Float from string: 3.14
Bool from string: true
String from Sprintf: 123
String from rune: A

Type Inference

Go has built-in type inference, which means the compiler can often determine the type of a variable based on the value assigned to it.

go
package main

import "fmt"

func main() {
// Type inference with the := operator
x := 42 // int
y := 3.14 // float64
z := "hello" // string
w := true // bool

fmt.Printf("x type: %T, value: %v
", x, x)
fmt.Printf("y type: %T, value: %v
", y, y)
fmt.Printf("z type: %T, value: %v
", z, z)
fmt.Printf("w type: %T, value: %v
", w, w)

// Type inference with var
var a = 100
var b = "world"

fmt.Printf("a type: %T, value: %v
", a, a)
fmt.Printf("b type: %T, value: %v
", b, b)

// Multiple variable declaration with type inference
c, d := 5, "five"
fmt.Printf("c type: %T, value: %v
", c, c)
fmt.Printf("d type: %T, value: %v
", d, d)
}

Output:

x type: int, value: 42
y type: float64, value: 3.14
z type: string, value: hello
w type: bool, value: true
a type: int, value: 100
b type: string, value: world
c type: int, value: 5
d type: string, value: five

Zero Values in Go

In Go, variables declared without an explicit initial value are given their "zero value", which depends on the type.

go
package main

import "fmt"

func main() {
var i int
var f float64
var b bool
var s string
var p *int // Pointer to int

fmt.Printf("Zero value for int: %v
", i)
fmt.Printf("Zero value for float64: %v
", f)
fmt.Printf("Zero value for bool: %v
", b)
fmt.Printf("Zero value for string: %q
", s) // %q for quoted string
fmt.Printf("Zero value for pointer: %v
", p)

// Zero values for composite types
var arr [3]int
var slice []int
var m map[string]int

fmt.Printf("Zero value for array: %v
", arr)
fmt.Printf("Zero value for slice: %v, nil? %v
", slice, slice == nil)
fmt.Printf("Zero value for map: %v, nil? %v
", m, m == nil)
}

Output:

Zero value for int: 0
Zero value for float64: 0
Zero value for bool: false
Zero value for string: ""
Zero value for pointer: <nil>
Zero value for array: [0 0 0]
Zero value for slice: [], nil? true
Zero value for map: map[], nil? true

Type Definitions and Type Aliases

Go allows you to create new types based on existing ones, as well as type aliases.

go
package main

import "fmt"

// Type definition
type Celsius float64
type Fahrenheit float64

// Type alias (Go 1.9+)
type Temperature = float64

func main() {
var c Celsius = 25.0
var f Fahrenheit = 77.0

fmt.Printf("Celsius: %v°C, type: %T
", c, c)
fmt.Printf("Fahrenheit: %v°F, type: %T
", f, f)

// Type conversion is required, even though both are based on float64
c = Celsius((f - 32) * 5 / 9)
fmt.Printf("Converted: %v°C
", c)

// Type alias
var t Temperature = 22.5
fmt.Printf("Temperature: %, type: %T
", t, t)

// No conversion needed for type alias
var f2 float64 = t
fmt.Printf("float64 from Temperature: %v, type: %T
", f2, f2)
}

Output:

Celsius: 25°C, type: main.Celsius
Fahrenheit: 77°F, type: main.Fahrenheit
Converted: 25°C
Temperature: 22.5°, type: float64
float64 from Temperature: 22.5, type: float64

Real-World Applications

Let's explore some real-world applications of Go data types.

Temperature Converter

go
package main

import (
"fmt"
"strconv"
)

// Define custom types for temperature units
type Celsius float64
type Fahrenheit float64
type Kelvin float64

// Conversion methods
func (c Celsius) ToFahrenheit() Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}

func (c Celsius) ToKelvin() Kelvin {
return Kelvin(c + 273.15)
}

func (f Fahrenheit) ToCelsius() Celsius {
return Celsius((f - 32) * 5 / 9)
}

func (f Fahrenheit) ToKelvin() Kelvin {
return f.ToCelsius().ToKelvin()
}

func (k Kelvin) ToCelsius() Celsius {
return Celsius(k - 273.15)
}

func (k Kelvin) ToFahrenheit() Fahrenheit {
return k.ToCelsius().ToFahrenheit()
}

// String methods for nice printing
func (c Celsius) String() string {
return fmt.Sprintf("%.2f°C", c)
}

func (f Fahrenheit) String() string {
return fmt.Sprintf("%.2f°F", f)
}

func (k Kelvin) String() string {
return fmt.Sprintf("%.2fK", k)
}

func main() {
// Example usage
var waterBoiling Celsius = 100
fmt.Println("Water boiling point:")
fmt.Println("Celsius:", waterBoiling)
fmt.Println("Fahrenheit:", waterBoiling.ToFahrenheit())
fmt.Println("Kelvin:", waterBoiling.ToKelvin())

fmt.Println("Temperature converter:")

temps := []string{"0", "25", "100"}
for _, t := range temps {
value, _ := strconv.ParseFloat(t, 64)
c := Celsius(value)
fmt.Printf("%s = %s = %s", c, c.ToFahrenheit(), c.ToKelvin())
}
}

Output:

Water boiling point:
Celsius: 100.00°C
Fahrenheit: 212.00°F
Kelvin: 373.15K

Temperature converter:
0.00°C = 32.00°F = 273.15K
25.00°C = 77.00°F = 298.15K
100.00°C = 212.00°F = 373.15K

Contact Book Application

go
package main

import (
"fmt"
"strings"
)

// Define struct types for our contact book
type ContactInfo struct {
Email string
Phone string
Address string
}

type Contact struct {
ID int
FirstName string
LastName string
Info ContactInfo
Tags []string
}

// ContactBook type using a map with int keys (contact IDs)
type ContactBook map[int]Contact

// Method to add a contact
func (cb ContactBook) AddContact(contact Contact) {
cb[contact.ID] = contact
}

// Method to search contacts by name
func (cb ContactBook) SearchByName(query string) []Contact {
query = strings.ToLower(query)
var results []Contact

for _, contact := range cb {
firstName := strings.ToLower(contact.FirstName)
lastName := strings.ToLower(contact.LastName)

if strings.Contains(firstName, query) || strings.Contains(lastName, query) {
results = append(results, contact)
}
}

return results
}

// Method to search contacts by tag
func (cb ContactBook) SearchByTag(tag string) []Contact {
tag = strings.ToLower(tag)
var results []Contact

for _, contact := range cb {
for _, contactTag := range contact.Tags {
if strings.ToLower(contactTag) == tag {
results = append(results, contact)
break
}
}
}

return results
}

// Method to print a contact
func (c Contact) Print() {
fmt.Printf("ID: %d", c.ID)
fmt.Printf("Name: %s %s", c.FirstName, c.LastName)
fmt.Printf("Email: %s", c.Info.Email)
fmt.Printf("Phone: %s", c.Info.Phone)
fmt.Printf("Address: %s", c.Info.Address)
fmt.Printf("", strings.Join(c.Tags, ", "))
fmt.Println("-------------------")
}

func main() {
// Create a new contact book
contactBook := make(ContactBook)

// Add some contacts
contactBook.AddContact(Contact{
ID: 1,
FirstName: "John",
LastName: "Doe",
Info: ContactInfo{
Email: "[email protected]",
Phone: "555-1234",
Address: "123 Main St, Anytown",
},

})

contactBook.AddContact(Contact{
ID: 2,
FirstName: "Jane",
LastName: "Smith",
Info: ContactInfo{
Email: "[email protected]",
Phone: "555-5678",
Address: "456 Oak St, Anytown",
},

})

contactBook.AddContact(Contact{
ID: 3,
FirstName: "Michael",
LastName: "Johnson",
Info: ContactInfo{
Email: "[email protected]",
Phone: "555-9012",
Address: "789 Pine St, Othertown",
},

})

// Demonstrate searching
fmt.Println("All contacts:")
for _, contact := range contactBook {
contact.Print()
}

fmt.Println("Search for 'john':")
results := contactBook.SearchByName("john")
for _, contact := range results {
contact.Print()
}

fmt.Println("Search for tag 'work':")
results = contactBook.SearchByTag("work")
for _, contact := range results {
contact.Print()
}
}

Output:

All contacts:
ID: 1
Name: John Doe
Email: [email protected]
Phone: 555-1234
Address: 123 Main St, Anytown

-------------------
ID: 2
Name: Jane Smith
Email: [email protected]
Phone: 555-5678
Address: 456 Oak St, Anytown

-------------------
ID: 3
Name: Michael Johnson
Email: [email protected]
Phone: 555-9012
Address: 789 Pine St, Othertown

-------------------

Search for 'john':
ID: 1
Name: John Doe
Email: [email protected]
Phone: 555-1234
Address: 123 Main St, Anytown

-------------------

Search for tag 'work':
ID: 1
Name: John Doe
Email: [email protected]
Phone: 555-1234
Address: 123 Main St, Anytown

-------------------
ID: 3
Name: Michael Johnson
Email: [email protected]
Phone: 555-9012
Address: 789


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)