Variables & Data Types
What is a variable?
A variable is a named storage location for a value. In Go, every variable has a type. For example, string stores text, while int stores whole numbers.
Data types
Go gives us several built-in data types.
| Type | Use | Zero value |
|---|---|---|
bool | Stores true or false. | false |
string | Stores UTF-8 text. | "" |
int | Stores a signed integer. Its size is either 32 or 64 bits depending on the platform. | 0 |
uint | Stores an unsigned integer. Its size is either 32 or 64 bits depending on the platform. | 0 |
int8 | Stores an 8-bit signed integer from -128 to 127. | 0 |
uint8 | Stores an 8-bit unsigned integer from 0 to 255. | 0 |
int16 | Stores a 16-bit signed integer from -32768 to 32767. | 0 |
uint16 | Stores a 16-bit unsigned integer from 0 to 65535. | 0 |
int32 | Stores a 32-bit signed integer. | 0 |
uint32 | Stores a 32-bit unsigned integer. | 0 |
int64 | Stores a 64-bit signed integer. | 0 |
uint64 | Stores a 64-bit unsigned integer. | 0 |
uintptr | Stores an integer large enough to hold the bit pattern of a pointer. | 0 |
float32 | Stores a 32-bit floating-point number. | 0 |
float64 | Stores a 64-bit floating-point number. | 0 |
complex64 | Stores a complex number with float32 real and imaginary parts. | (0+0i) |
complex128 | Stores a complex number with float64 real and imaginary parts. | (0+0i) |
byte | Alias for uint8. | 0 |
rune | Alias for int32; commonly used for Unicode code points. | 0 |
error | Represents an error value. | nil |
Zero value
Unlike languages where an uninitialized variable may hold null or undefined, Go gives every variable a zero value. A bool becomes false, an int becomes 0, and a string becomes an empty string.
Declaring a variable
To declare a single variable, you can use the following syntax.
var variableName dataType = initialValue
initialValueis optional. If you do not give an initial value in the variable assignment, it will hold the zero-value of its data type.
var integer1 int = 15
var integer2 int8 = -25
var integer3 int32 // default 0
var float1 float32 = 63.2
var string1 string = "Hello World!"
var boolean1 bool // default false
var boolean2 bool = true
You can later assign a new value to an existing variable using normal assignment.
// variableName = newvalue
integer1 = -15
integer2 = 25
integer3 = 965
float1 = -52.99
string1 = "Hello There :)"
boolean1 = true
boolean2 = false
Variable name convention
Go recommends writing variable names in simple word or camelCase. Even under_score variable names are valid, they are not idiomatic.
Type Inference
Even though Go is statically typed, we do not always have to write the type manually. If the initial value is clear, Go can infer the type.
var variableName = initialValue
Go infers the type from initialValue. This syntax only works when the variable has an initial value.
var integer1 = 52 // int
var string1 = "Hello World" // string
var boolean1 = false // bool
Short-hand notation
Go also provides a shorthand syntax. With this form, we can drop both var and the explicit type.
variableName := initialValue
When Go sees :=, it knows we are declaring a new variable with an initial value. We cannot use this syntax to assign a value to an already declared variable unless at least one variable on the left side is new.
integer1 := 52 //int
string1 := "Hello World" //string
boolean1 := false //bool
Multiple variable declarations
You can declare multiple variables of the same data type in a single statement using the syntax below.
var var1, var2, var3 int
You can also assign initial values to these variables. You must assign all the variables with an initial value.
var var1, var2, var3 int = 1, 2, 3
You can also drop the data type declaration in this syntax and let Go infer the types from the initial values. This lets us declare variables of different types on a single line.
var var1, var2, var3 = 1, 2.2, false
You can also use the shorthand notation instead of the above statement.
var1, var2, var3 := 1, 2.2, false
You can also use multiple lines to declare variables of different data types with a single use of var keyword. In this syntax, you do not have to provide initial values to all the variables.
var (
var1 = 50
var2 = 25.22
var3 string = "Telefonía"
var4 bool
)
More on short-hand notation
The shorthand notation syntax := can only be used inside a function body. Also, it can only be used when at least one of the variables on the left side of := is newly declared.
Shorthand syntax will come in handy when the value of a variable is assigned at runtime and the data type of the value is not known beforehand. For example, a value received from a function call (example below).
package main
import (
"fmt"
"math"
)
func main() {
a, b := 145.8, 543.8
c := math.Min(a, b)
fmt.Println("The minimum value is ", c)
}
// The minimum value is 145.8
Type conversion
In Go, you cannot perform add or subtract (or any arithmetic) operations on variables to two different data types. So variables first must be converted to the same data type.
In Go, this conversion is very simple. Just wrap any variable inside parenthesis with the data type prefix that you want to convert that variable into. The syntax to convert data to a data type is type(*var*).
var1 := 10 // int
var2 := 10.5 // float64// *illegal*
// var3 := var1 + var2// *legal*
var3 := var1 + int(var2) *// var3 => 20*
If you have a string (which is a slice of byte or uint8 values), you can convert it to a different integer data type using []type() syntax.
var1 := "Hello"
var2 := []int32(va1)
Defining a type
Go provides some built-in types but we can create our own types derived from these built-in types as well as aliases to existing types.
Derived Type
You create a new derived type from any built-in or user-defined type using type keyword.
type newType fromType
In the this syntax, newType is a new type derived from the fromType. The newType has all the properties of the fromType, however, now we can add additional properties on it without modifying fromType.
You can assign any fromType value to a newType variable or constant, however, fromType and newType types are not comparable since they are two different types. But we can use type conversion to convert the value fromType to newType and vice-versa.
For example, if you want to define your own data type float which should have all the properties of float64 type, then you can define it like below.
type float float64
In the program below, Go will create float type which will be derived from the float64 built-in type. You can use this type as a normal Go data type and declare variables of the float data type.
Go does not have a built-in float type name. If we want that name in our own code, we can define it as a custom type derived from float64.
package main
import "fmt"
type float float64
func main() {
var f float = 52.2
fmt.Printf("f has value %v and type %T\n", f, f)
}
// f has value 52.2 and type main.float
In this program, we define float as a type derived from float64, then create a variable f of that new type.
f has value 52.2 and type main.float
This program prints
main.floatbecausefloatis a custom type and it must belong to a package. Type can be defined anywhere in the program, like in a function or a condition block.
If you define a new type with beginning with a lowercase letter, then it won’t be exported from the package and other packages won’t be able to use it. We will discuss this in packages and modules tutorials.
package main
import "fmt"
type float float64
func main() {
var f float = 52.2
var g float64 = 52.2
fmt.Println("f == g", f == g)
}
Also, float is not automatically compatible with float64. They share the same underlying representation, but they are still different types.
package main
import "fmt"
type float float64
func main() {
var f float = 52.2
var g float64 = 52.2
fmt.Println("f == g", f == g)
}
// ./prog.go:10:29: invalid operation: f == g (mismatched types float and float64)
So this program does not compile. The compiler reports this error.
invalid operation: f == g (mismatched types float and float64)
However, you are free to compare a variable of any type with a compatible value directly. For example, f == 52.2 returns true because 52.2 is a valid float type as well.
Type Alias
A type alias is an alternative name for an existing type. We define a type alias using the following syntax.
type aliasName = oldType
In this syntax, aliasName is another name for the already defined type oldType. Unlike a derived type, an alias does not create a new type.
package main
import "fmt"
type float = float64
func main() {
var f float = 52.2
var g float64 = 52.2
fmt.Println("f == g", f == g)
}
// f == g true
In this program, float is just another name for the built-in float64 type. Since these are the same type, their values are comparable.
Unless very necessary, you should not create a type alias since it doesn’t do anything important. However, type alias can be useful when you have to refactor a large amount of code (ref).
What is a constant
A constant is a named value that cannot be changed. Constants are declared with the const keyword and must have an initial value.
const const_name [data_type] = fixed_value
data_type is optional because Go can infer it from fixed_value.
In Go, constant value must be known during compile time. If you are trying to assign value returned by a function call, that won’t work.
const a = math.Sqrt(4)is an illegal statement as value ofais computed during run time.
Multiple constants declaration
Like variables, multiple constants can be declared in one statement.
const const_1, const_2 = value_1, value_2
Or you can use the multiple-line variable declaration syntax.
const (
const_1 = value_1
const_2 = value_2
)
In a parenthesized const declaration, Go can implicitly repeat the previous expression.
const (
a = 1 // a == 1
b = 2 // b == 2
c // c == 2
d // d == 2
)
The iota expression
Go provides a keyword iota that can be used when declaring enumerated constants. This keyword yields an incremented value by 1 starting from 0, each time it is used.
const (
a = iota // a == 0
b = iota // b == 1
c = iota // c == 2
d // d == 3 (implicitely d = iota)
)
Use _ (blank identifier) to skip a value. _ will receive the value of iota + 1 but will save the value in the garbage scope.
const (
a = iota + 1 // a == 1
_ // (implicitly _ = iota + 1 )
b // b == 3 (implicitly b = iota + 1 )
c // c == 4 (implicitly c = iota + 1 )
)
package main
import "fmt"
func main() {
const (
a = iota + 1 // a == 1
_ // (implicitly _ = iota + 1 )
b // b == 3 (implicitly b = iota + 1 )
c // c == 4 (implicitly c = iota + 1 )
)
fmt.Println(a, b, c)
}
// 1 3 4
One crucial to remember here is that, iota can only be used with variables defined with const. iota will always return an integer of type int. The scope of iota is restricted to the const block.One crucial to remember here is that, iota can only be used with variables defined with const. iota will always return an integer of type int. The scope of iota is restricted to the const block.
const a = iota // 0
const b = iota // 0
const (
c = iota // 0
d = iota // 1
)
const (
e = iota // 0
f = iota // 1
)
As you can see from this example, iota value resets to 0 whenever const keyword appears in the program.
Numeric Expressions
Numeric constants (like 1, 2, 3, etc.) are free to be mixed and matched in expressions and a type is needed only when they are assigned to variables or used in a place where a data type has to be explicitly declared.
But the result of any mathematical expression depends on the data type of the operands. For example, the division of two integers will be an integer.
var a = 11/2 // a == 5
This happens as both operands hold data type of int. If you change any of the operand’s data type to float32, the result will be a float32 value.
var a = 11.0/2 // 5.5 (float64)
var b = float32(11)/2 // 5.5 (float32)