Learning Center
Go

Variables in Go

27 Jan 2025
Hostman Team
Hostman Team

Variables are named values stored in specific areas of memory and used during program execution.

Go (also known as Golang) is a statically typed programming language. This means that once a variable is declared, its type is fixed and cannot be changed.

Variables can have various types, each with its own purpose and characteristics.
Go provides several basic data types, which form the foundation of the language's logic:

  • Integer
  • Floating-point
  • String
  • Boolean

Additionally, Go supports composite data types:

  • Arrays
  • Slices
  • Structures
  • Maps

There are also several auxiliary types:

  • Pointers
  • Interfaces

Besides these, Go (similar to C++) includes a Standard Library (std) containing many predefined types.

You can find more detailed information about variable types in Go in a separate article. For instructions on installing Go on Linux, Windows, or macOS, refer to the Hostman guides

All the code examples in this tutorial were tested using Go version 1.21.3.

Compiling and Running Code
Copy link

All the code examples in this guide are run in separate files with the .go extension.

First, create a new file:

sudo nano example.go

Next, fill it with code inside the main() function, including any necessary modules:

package main
import "fmt"

func main() {
    // start of example
    var number int = 10
    fmt.Println(number)
    // end of example
}

Then run the file:

go run example.go

Declaring a Variable
Copy link

There are different ways to declare a variable in Go before using it—ranging from a full form, explicitly specifying the parameters (or multiple parameters) of the variable, to a shorthand form that uses automatic type inference and initialization.

The choice of declaration method depends on the context. However, it’s generally recommended to use the most concise and automatic form whenever possible, as this reduces the likelihood of programmer errors by shifting some of the responsibility to the language's interpreter.

Using the var Keyword
Copy link

The most explicit way to declare a variable in Golang is by using the var keyword, followed by the variable name, type, and value:

var some_variable int = 5

However, if the variable is initialized with a value, you can omit the explicit type:

var some_variable = 5

You can also declare a variable without assigning a value, but in this case, you must specify the type:

var some_variable int
some_variable = 5

In all of these examples:

  • var — the keyword for declaring a variable
  • some_variable — the variable's name
  • int — the variable's type
  • 5 — the variable's value

For example, this is how you can declare string variables:

var some_name string = "John"

The following declaration will result in an error:

// ERROR: no value or type specified during declaration
var some_name
some_name = "John"

It’s important to note that type inference is only possible during the initial declaration of the variable when the interpreter allocates the appropriate amount of memory for its value.

Short Form :=
Copy link

Despite Go's strict static typing, it allows variables to be declared in a more concise form without explicitly specifying their parameters:

some_variable := 5

In this case, the interpreter understands that it needs to automatically infer the variable type based on the assigned value.

However, this shorthand declaration is only allowed inside a function (including main()); it cannot be used outside a function:

package main

// ERROR: short form declaration outside of a function
some_variable := 5

func main() {
    // OK: short form declaration inside a function
    other_variable := 10
}

It’s important to understand the distinction between declaring a variable (with initialization) and assigning a value to it:

package main

func main() {
    some_variable := 5 // this is declaration and initialization (colon is present)
    some_variable = 50 // this is assignment (no colon)

    other_variable = 7 // ERROR: this is assignment (no colon) to an undeclared variable
}

For example, you can declare (and initialize) several variables sequentially:

age := 50 // variable of type int
name := "John" // variable of type string
occupation := "Just a guy" // variable of type string
height := 190.5 // variable of type float32

You cannot use the := operator together with the var keyword. Doing so will result in an error:

var someVariable int := 5 // ERROR
var someVariable := 5 // ERROR

Excluding the var keyword but still explicitly specifying the type will still result in an error:

someVariable int := 5 // ERROR

Multiple Variables
Copy link

In Go, you can declare multiple variables in one line or block.

For example, you can use the var keyword with a single type for all declared variables:

var width, height, depth int = 100, 200, 300

You can also separate the declaration of variables and their assignment:

var width, height, depth int
width, height, depth = 100, 200, 300

If the variable types differ, the interpreter can automatically infer their types:

var name, age, fired = "John", 50, false

Similarly, you can use the short form for multiple variables:

name, age, fired := "John", 50, false

In this case, there is no var keyword, nor are the types of the variables specified.

Another way to declare multiple variables is by using a block:

var (
    name   string  = "John"
    age    int     = 50
    height float64 = 190
    fired  bool    = false
)

By the way, you can format block declarations using spaces in such a way that names, types, and values align in columns, improving code readability:

var (
    name     string      = "John"
    age      int         = 50
    height   float64     = 190.5
    fired    bool        = false
)

The block declaration has no particular utility significance. It’s just syntactic sugar that:

  • Improves code readability by grouping important variables in one place.
  • Improves code cleanliness by avoiding repeated use of the var keyword for each variable.
  • Improves code maintainability by simplifying the search and modification of variable parameters.

Thus, block declaration is justified only when you need to group several key variables, simplifying their visual perception in a code editor.

No Initialization
Copy link

In Go, it is possible to create a variable without initializing it. In this case, the variable is assigned a zero value corresponding to the specified type:

  • For int, float32, float64: 0, 0.0, 0.0
  • For bool: false
  • For string: ""
  • For pointers: nil

We can demonstrate this behavior of Go regarding variable declaration and initialization in the following script:

package main

import "fmt"

func main() {
    // Integer
    var numberInt int
    fmt.Println("Integer:", numberInt)

    // Floating-point number
    var numberFloat float32
    fmt.Println("Floating-point number:", numberFloat)

    // String
    var text string
    fmt.Println("String:", text)

    // Boolean
    var condition bool
    fmt.Println("Boolean:", condition)

    // Array
    var array [5]int
    fmt.Println("Array:", array)

    // Slice
    var cut []int
    fmt.Println("Slice:", cut)

    // Struct
    type S struct {
        name    string
        size    int
        address string
    }
    var structure S
    fmt.Println("Struct:", structure)

    // Map
    var dictionary map[int]int
    fmt.Println("Map:", dictionary)

    // Pointer
    var pointer *int
    fmt.Println("Pointer:", pointer)
}

The console output will be as follows:

Integer: 0  
Floating-point number: 0  
String:  
Boolean: false  
Array: [0 0 0 0 0]  
Slice: []  
Struct: { 0}  
Map: map[]  
Pointer: <nil>

As you can see, variables of different types are automatically initialized with zero (or empty) values wherever possible.

Naming Conventions
Copy link

In Golang, variable names can either start with a Latin letter or an underscore (_):

onething := 123  // OK
Onething := 123  // OK
_onething := 123 // OK
__onething := 123 // OK
1thing := 123 // ERROR

Additionally, variable names have a functional feature: names starting with an uppercase letter are visible in other packages, while names starting with a lowercase letter are not.

There are also several universal naming conventions across programming languages, including Go:

  • Snake Case
  • Camel Case
  • Pascal Case
  • Kebab Case (not supported in Go)

Snake Case

In Snake Case, the variable name looks like this:

some_random_variable := 123 // lowercase
SOME_RANDOM_VARIABLE := 123 // uppercase

Camel Case

In Camel Case, the variable name looks like this:

someRandomVariable := 12

Pascal Case

In Pascal Case, the variable name looks like this:

SomeRandomVariable := 123

Kebab Case

In Kebab Case, the variable name looks like this:

// ERROR
some-random-variable := 123 // lowercase
SOME-RANDOM-VARIABLE := 123 // uppercase

However, Go doesn't support the Kebab Case style due to the hyphen character, which is reserved for the subtraction operation.

Example: Declaring Multiple Variables
Copy link

Let’s further explore all the aforementioned ways of declaring variables in Golang in this script example:

package main

import "fmt"

func main() {
	// Explicit declaration with type specification
	var age int = 50
	fmt.Println("Age:", age)

	// Explicit declaration with type inference
	var height = 190.5
	fmt.Println("Height:", height)

	// Short declaration
	name := "John"
	fmt.Println("Name:", name)

	// Explicit declaration of multiple variables
	var width, depth int = 100, 200
	fmt.Println("Width:", width, "Depth:", depth)

	// Explicit declaration without initialization
	var distance int
	fmt.Println("Distance:", distance)

	// Block declaration of multiple variables
	var (
		occupation string = "Welder"
		category   float32 = 3.4
		license    bool
	)
	fmt.Println("Occupation:", occupation, "Category:", category, "License:", license)
}

The result of running this code will be the following output in the console:

Age: 50
Height: 190.5
Name: John
Width: 100 Depth: 200
Distance: 0
Occupation: Welder Category: 3.4 License: false
  • The var keyword is required for explicit variable declaration, especially in the global scope.
  • The := operator is used for short variable declarations, particularly within functions.
  • The block () syntax is used for readable declaration of multiple variables.

It's important to remember that Go emphasizes minimalism and concise syntax. Therefore, the most compact form of notation should be used wherever possible.

This reduces errors and issues while maintaining the cleanliness and readability of the code.

Variable Initialization
Copy link

Typically, when a variable is declared, it is manually initialized with a specific value. The initialization of different types has syntactic differences.

Number

Numerical variables are initialized by assigning a numerical value, which is syntactically simple:

// int
var someNumber int = 5

// float32
otherNumber := 10.0

A number can be initialized with another number:

// int
var someNumber int = 5
var otherNumber int = someNumber

// int
oneMoreNumber := someNumber

String

String variables are initialized by assigning a sequence of characters enclosed in double quotes:

// string
var someString string = "Some programmer was here"

A string can also be initialized with another string:

// string
var someString string = "Some programmer was here"
var otherString string = someString

// string
oneMoreString := someString

Boolean

Initializing boolean variables is similar to initializing numeric and string variables, except that the value used is the keyword true or false:

// bool
var someBool bool = true

Similarly, boolean variables can be initialized with other boolean variables:

// bool
var someBool bool = true
var otherBool bool = someBool

// bool
oneMoreBool := someBool

Array

There are several ways to initialize an array. The simplest one is through sequential access to the elements:

// array
var languages [3]string

languages[0] = "Golang"
languages[1] = "Python"
languages[2] = "Rust"

A more complex method is using a composite literal. A composite literal is a compact syntax for initializing any composite (struct-like) type, which avoids assigning each element individually.

Thus, the array can be initialized in one step:

var languages = [3]string{"Golang", "Python", "Rust"}

Or using the shorthand form:

languages := [3]string{"Golang", "Python", "Rust"}

You can also partially initialize array elements:

// array size 5, but only 3 elements initialized
languages := [5]string{"Golang", "Python", "Rust"}

languages[3] = "Java"
languages[4] = "C++"

To make the initialization of a large array more readable, you can format it like this:

languages := [5]string{
	"Golang",
	"Python",
	"Rust",
	"Java",
	"C++", // the comma at the end is REQUIRED
}

By the way, an array can be initialized with another array, copying all of its elements:

languages := [3]string{"Golang", "Python", "Rust"}
otherLanguages := languages

It’s important to understand that copying an array also occurs when it is passed to a function:

package main

import "fmt"

func change(languages [5]string) {
	for i := range languages {
		languages[i] = "[" + languages[i] + "]"
	}
}

func main() {
	languages := [5]string{
		"Golang",
		"Python",
		"Rust",
		"Java",
		"C++",
	}

	change(languages)
	fmt.Println(languages)
}

The output in the console will be:

[Golang Python Rust Java C++]

Thus, only the copy of the array inside the change() function was modified, not the original array from the main() function.

However, explicit initialization of an array with another array is possible only if both arrays have the same length and type:

languages := [3]string{"Golang", "Python", "Rust"}

var otherLanguages [3]string = languages // OK
var oneMoreLanguages [4]string = languages // ERROR

Additionally, in Go, you can create arrays from an arbitrary number of other arrays. You can initialize elements of such arrays both sequentially:

var matrix [2][2]string

matrix[0][0] = "a"
matrix[0][1] = "b"
matrix[1][0] = "c"
matrix[1][1] = "d"

Or using a composite literal:

var matrix = [2][2][2]string{{{"a", "b"}, {"c", "d"}}, {{"e", "f"}, {"g", "h"}}}

As shown, the second option takes up less space, but the syntax is more complex.

Slice

A slice is initialized the same way as an array:

var languages = []string{"Golang", "Python", "Rust"}

However, unlike an array, a slice can be initialized with another slice of arbitrary length:

var languages = []string{"Golang", "Python", "Rust"}
var otherLanguages []string = languages

Map

Maps are initialized using a composite literal with the type of the key and value specified. The content is listed using commas and separated by a colon:

var languages = map[string]string{"first": "Golang", "second": "Python", "third": "Rust"}

You can also use the shorthand declaration and a more readable initialization format:

languages := map[string]string{
	"first": "Golang",
	"second": "Python",
	"third": "Rust", // the comma at the end is MANDATORY
}

However, initializing a map with another map does not copy the elements; instead, it makes them shared:

package main

import "fmt"

func main() {
	languages := map[string]string{"first": "Golang", "second": "Python", "third": "Rust"}
	otherLanguages := languages

	fmt.Println(languages)
	fmt.Println(otherLanguages)

	otherLanguages["first"] = "C++"

	fmt.Println(languages)
	fmt.Println(otherLanguages)

	delete(otherLanguages, "second")

	fmt.Println(languages)
	fmt.Println(otherLanguages)
}

The console output of this example will be:

map[first:Golang second:Python third:Rust]
map[first:Golang second:Python third:Rust]
map[first:C++ second:Python third:Rust]
map[first:C++ second:Python third:Rust]
map[first:C++ third:Rust]
map[first:C++ third:Rust]

Pointer

Pointers can only be initialized with the address of a variable of the same type:

var variable int = 15
var pointer *int = &variable

The ampersand (&) symbol is used to get the address of any variable:

package main

import "fmt"

func main() {
	var variable int = 15
	var pointer *int = &variable

	fmt.Println(pointer)
}

The console output of this example will look something like:

0xc000104040

You can also use shorthand notation to initialize pointers:

variable := 15
pointer := &variable

To access the value stored at the address of a pointer, you need to dereference it using the asterisk (*):

package main

import "fmt"

func main() {
	var variable int = 15
	var pointer *int = &variable

	fmt.Println(*pointer)
}

In this case, the console output will show:

15

Thus, you can assign new values to a variable located at the address of the pointer:

package main

import "fmt"

func main() {
	var variable int = 15
	var pointer *int = &variable

	*pointer = 5

	fmt.Println(*pointer)
}

The console will display:

5

Finally, a pointer can be initialized with an anonymous object in memory. This is done using the new() function, which returns the address of the allocated memory:

variable := new(int)
*variable = 15

You don't need to manually delete the allocated memory — the garbage collector automatically handles this.

Structure

A structure can be initialized either with explicitly specified values in order:

type something struct {
	first  string
	second int
}
var structure something = something{"John", 15}

Or with explicitly specified values by key names:

type something struct {
	first  string
	second int
}

var structure something = something{second: 15, first: "John"}

Alternatively, you can choose not to specify any values, which will automatically initialize all fields to their zero values:

package main

import "fmt"

type something struct {
	first  string
	second int
}

func main() {
	var structure something = something{}

	fmt.Println(structure)

	structure.first = "John"
	structure.second = 15

	fmt.Println(structure)
}

In this case, the console output will be:

{ 0}
{John 15}

Branching Based on Variables
Copy link

Variables play a central role in branching. Different parts of the program's code are executed based on their values (conditions).

if/else
Copy link

The most basic conditional construct is created using the if/else statements. Here's the simplest condition:

a := 5
b := 10

if a < b {
	fmt.Println("A is less than B")
}

For example, you can use a simple condition to check a pointer:

var pointer *int

if pointer == nil {
	fmt.Println("No address")
}

A more complex form would look like this:

a := 10
b := 5

if a < b {
	fmt.Println("A is less than B")
} else {
	fmt.Println("A is greater than B")
}

You can create even more complex constructs by combining else and if:

a := 10
b := 5

if a < b {
	fmt.Println("A is less than B")
} else if a > b {
	fmt.Println("A is greater than B")
} else {
	fmt.Println("A is equal to B")
}

Multiple if/else expressions can be used:

a := 12

if a < 5 {
	fmt.Println("A is less than 5")
} else if a < 10 {
	fmt.Println("A is less than 10")
} else if a < 20 {
	fmt.Println("A is less than 20")
} else {
	fmt.Println("A is in superposition")
}

switch
Copy link

Another way to branch is using the switch construct, where possible values of a variable are defined, and actions are performed if there's a match:

a := 1

switch a {
	case 0:
		fmt.Println("A is 0")
	case 1:
		fmt.Println("A is 1")
	case 2:
		fmt.Println("A is 2")
}

The default section can be used to define an action that runs if no match occurs:

a := 3

switch a {
	case 0:
		fmt.Println("A is 0")
	case 1:
		fmt.Println("A is 1")
	case 2:
		fmt.Println("A is 2")
	default:
		fmt.Println("A is in superposition")
}

You can also combine multiple possible matches into one section:

a := 1

switch a {
	case 0, 1, 2:
		fmt.Println("A is either 0, 1, or 2")
	default:
		fmt.Println("A is in superposition")
}

Useful Functions
Copy link

Go has many utility functions for working with variables. In this guide, we'll cover just the basic ones.

Environment Variables
Copy link

Go provides special system functions that allow you to set and get environment variables:

package main

import (
	"fmt"
	"os"
)

func main() {
	os.Setenv("SOMEVAR", "1") // Set an environment variable
	fmt.Println("SOMEVAR:", os.Getenv("SOMEVAR")) // Read an environment variable
}

Time Variables
Copy link

Often, the program logic requires measuring time. Go has a corresponding tool for this — the time type.

Time is a broad topic by itself. To learn more about the time package, you can check the official documentation. This guide will show how to get the current time in different formats:

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("Current time:", time.Now())
	fmt.Println("Current time (UTC):", time.Now().UTC())
	fmt.Println("Current time (Unix):", time.Now().Unix())
}

The console output will look something like this:

Current time: 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
Current time (UTC): 2009-11-10 23:00:00 +0000 UTC
Current time (Unix): 1257894000

You can also specify specific time parameters:

package main

import (
	"fmt"
	"time"
)

func main() {
	timeNow := time.Now()
	fmt.Println("Full time:", timeNow)

	fmt.Println("Year:", timeNow.Year())
	fmt.Println("Month:", timeNow.Month())
	fmt.Println("Day:", timeNow.Day())
	fmt.Println("Hour:", timeNow.Hour())
	fmt.Println("Minutes:", timeNow.Minute())
	fmt.Println("Seconds:", timeNow.Second())
}

In this case, the console output will be:

Full time: 2024-11-15 23:46:09.157929822 +0000 UTC m=+0.000031801
Year: 2024
Month: November
Day: 15
Hour: 23
Minutes: 23
Seconds: 9

Adding and Removing Elements from a Slice
Copy link

You can add elements to slices:

var languages = []string{"Golang", "Python", "Rust"}

languages = append(languages, "Java", "C++")
fmt.Println(languages)

This will append "Java" and "C++" to the languages slice.

You can also remove elements from slices:

var languages = []string{"Golang", "Python", "Rust"}

// Remove the 2nd element (index 1)
n := 1
languages = append(languages[:n], languages[n+1:]...)
fmt.Println(languages)

In this example, the second element is removed from the languages slice using slice operators, which create a new sequence from parts of the original slice.

Here’s an example of slicing a sequence:

package main

import "fmt"

func main() {
	var sequence = []string{"One", "Two", "Three", "Four", "Five"}
	newSequence := sequence[1:4] // Elements from index 1 to 3 become the new slice

	fmt.Println(newSequence)
}

The output in the console will be:

[Two Three Four]

Checking the Type of a Variable
Copy link

You can check the type of a variable using the TypeOf() function from the reflect package:

package main

import (
	"fmt"
	"reflect" // Package to determine the type
)

func main() {
	variableString := "string"
	variableInt := 5
	variableFloat64 := 1.5
	variableBool := true

	fmt.Println(reflect.TypeOf(variableString))
	fmt.Println(reflect.TypeOf(variableInt))
	fmt.Println(reflect.TypeOf(variableFloat64))
	fmt.Println(reflect.TypeOf(variableBool))
}

The console output for this example will be:

string
int
float64
bool

Variables in Strings
Copy link

Often, you need to insert a variable into a string. There are several ways to do this:

package main

import "fmt"

func main() {
	// METHOD 1
	stringPre := "human-readable"
	stringEnd1 := fmt.Sprintf("This is a %s string", stringPre)
	fmt.Println(stringEnd1)

	// METHOD 2
	stringEnd2 := "This is " + stringPre + " string"
	fmt.Println(stringEnd2)
}

The output in the console will be:

This is a human-readable string
This is a human-readable string

You can also combine numeric variables with strings:

package main

import "fmt"

func main() {
	name := "John"
	age := 50

	fmt.Printf("Hi, my name is %v and I'm %v years old.\n", name, age)
}

The output will be:

Hi, my name is John and I'm 50 years old.

Conclusion
Copy link

Like in most other programming languages, variables in Go are essential for storing data. Since data types differ from each other, Golang variables have several basic types, each having a specific representation in the computer's memory.

In this guide, we only covered the basic ways to work with variables. You can find more detailed (and comprehensive) information about types and their specifics in the official Golang documentation.

Additionally, the official Go package manager catalog provides information on many useful modules available for import into your project. One such module is the Standard Library.

Check out our app platform to deploy Go applications (such as Beego and Gin).