Introduction
In this article we look at Go data types, and how they are used to store values using variables and constants. We will also look at Pointers, a useful feature in Go, by demonstrating how they work and why they are useful.
Basic Types
In this section, we will look at the basic data types provided by Go.
int
The int
type is used to represent both positive and negative whole numbers. Its size is system-dependent (32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems). We also have integer types with specific sizes (int8
, int16
, int32
, and int64
) which are used when required, else we just use the int
keyword to create integers.
uint
The uint
type is used to represent positive whole numbers, just like int
its size is system dependent(32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems) and has types with specific sizes (uint8
, uint16
, uint32
and uint64
) which are used when required.
byte
Thisbyte
type is a built-in alias for uint8
and its basically used to represent a byte of data.
float32, float64
These types are used to represent numbers with decimal points. These types allocate 32 or 64 bits to store the value.
complex64, complex128
These types are used to represent numbers with real and imaginary parts. These types allocate 64 and 128 bits to store the value.
bool
The bool
type uses one of two values(true
and false
) to represent a Boolean truth.
string
The string
type is used to represent a sequence of characters.
rune
Thisrune
type is a built-in alias for int32
and it's used to represent a byte of data. Also, because of the int32
type it holds a signed 32-bit integer value which represents a single Unicode character.
Value Literals
We can express values in Go by defining the value directly in the source code. Some of its use cases include operands in expressions and arguments to functions.
In the preceding code, the first statement in the main function uses a string literal denoted by double quotes, passed as an argument to the fmt.Println
function. The other statement uses a literal int
value, whose result is passed as an argument to the fmt.Println
function.
We don't need to specify a type when using literal values because the compiler infers the type based on how the value is expressed.
Type | Examples |
int | There are four types of int literals—decmial(base 10), octal(base 8), hex(base 16) and binary(base 2). For, the value of twenty will be 20,-20, hex(0x14), octal(0o24), and (0b0010100) binary notation. |
uint | No uint literals. All literal whole numbers are treated as int values. |
byte | No byte literals. |
float64 | 20.2, -20.2, 1.2e10, 1.2e-10. Values can also be expressed in hex notation(0x2p10). |
bool | true, false |
string | "Hello" |
rune | 'B', '/n', '\u00A5', '¥' Characters, and escape sequences are enclosed in single quotes. |
Constants
Constants are values that can't be changed when defined. They are used to define values that won't change throughout the program execution. There are two ways to define constants in Go: typed and untyped constants.
Typed Constants
Typed constants are defined using the const
keyword, followed by a name, a type, and a value assignment.
The preceding code defines a string
constant called name
with the value "John Doe". Also, an int
constant called age
is created, it is used in an expression that is passed to the fmt.Println
function.
Untyped Constants
Unlike Javascript, Go doesn't perform automatic type conversions.
In the preceding code, the quantity
constant with an int
type is used in the expression passed to the fmt.Println
function to calculate a total price. But we get the following error.
.\main.go:11:24: invalid operation: quantity * (price + tax) (mismatched types int and float32)
Go's strict approach to type conversion means different types won't be automatically converted when used in an expression, hence the int
and float32
types cannot be mixed. But with the untyped constant feature this is possible because the Go compiler will perform limited automatic conversion.
An untyped constant is simply defined without a data type(as seen in the preceding code). Omitting the type when defining the quantity
constant tells the Go compiler to be dynamic with the type. Now, when the expression passed to the fmt.Println
function is evaluated, the quantity
value is converted to a float32.
Variables
Variables are containers for storing values, defined by the var
keyword, unlike constants the value assigned to a variable can be changed. A variable is declared using the var
keyword, a name, a type, and a value assignment.
In the preceding code, we defined the 'priceand
taxvariables and assigned
float32values to them. Then a new value is assigned to the
pricevariable via Go's assignment operator(Notice that we assigned the value
500to a floating point variable. This is possible because the *literal* value
500is an untyped constant that can be represented as a
float32` value).
Variables without Data Types
In Go type inference can be carried out on variables based on their values, which allows the types to be omitted when defining the variable.
In the preceding code, the value of the price
variable is set using a literal value(notice the type omission), and the value of price2
is set to the current value of price
. The compiler inspects the literal value assigned to the price and infers its type as float64
, the type of price2
is also inferred as float64
because its value is using the price
value.
N/B: Type omission doesn't work the same way in variables as it does for untyped constants. Go doesn't allow variables with different types to mix.
Literal floating point values are always inferred as float64
, which doesn't match the float32
type of the tax
variable, when the expression is passed fmt.Println
function we get an error.
Variables Without Value Assignment
To define a variable without assigning it an initial value, use the var
keyword followed by a name and type(the type cannot be omitted when there is no initial value).
N/B: Variables defined like this are assigned the zero value for the specified type.
From the output of the preceding code(0
, 180
), 0
is the zero value for the first value displayed in the output, followed by 180
the value assigned explicitly in the subsequent statement.
Short Variable Declaration Syntax
The short variable declaration syntax simply provides a simpler way of declaring variables.
This shorthand syntax specifies a name for the variable, a colon, an equal sign, and the initial value as seen in the preceding code. The var
keyword is not used, and a data type cannot be specified.
Go allows us to redefine a variable when we use the short syntax, as long as one of the other variables being defined doesn't already exist and the type of the variable doesn't change.
Blank Identifier
The blank identifier is used to declare and use unused variable(s). In Go it is illegal to define a variable and not use it.
In the preceding code, the blank identifier is used to denote the values that won't be used, it is used wherever using a name would create a variable that would not be later used.
Pointers
A pointer is an 8-byte integer pointing to a specific slot in memory. It is a variable whose value is a memory address, defined using the ampersand(&), known as the address operator, followed by the name of a variable.
Pointers have a typed and a value just like any other variable in Go. The value of the second_number
variable will be the memory address used by Go to store the value for the first_number
variable.
A pointer type depends on the type(prefixed with the asterisk character) of the variable that creates it. The type of the second_number
is *int
because it was created by applying the address operator to the first variable, whose value is int
. When you see the type *int
, you know it is a variable whose value is a memory address that stores an int
variable.
Pointers have a fixed type, which means you can create a pointer with an int
type, and change the value it points to, but you can't use it to point to the memory address that stores a different type(e.g. float64
). This constraint is necessary because pointers are not just memory addresses but, rather, memory addresses used to store a specific type of value.
Dereferencing a Pointer
Dereferencing a pointer means reading the value at the memory address that the pointer refers to, and it is done with an asterisk(*). Go will infer the pointer type just like it does with other types.
In the preceding code, the short variable declaration syntax is used to define the pointer. The second_number
pointer is followed, and the value at the memory location is incremented.
Thefirst_number
and second_number
variables have two distinct values. There is an int
value(200
) that can be accessed via first_number
, and an *int
value(memory address of first_number
) that can be followed(dereferenced), which will access the value stored in first_number
.
The *int
value is also a valid value that can be assigned to other variables, passed as an argument to a function, etc—as demonstrated in the following code.
The myNewPointer
variable is defined with an *int
type, an indication it points to an int
value. The next statement assigns the value of the second_number
variable to the new variable, meaning that the values of both second_number
and myNewPointer
are the memory location of the first value. Dereferencing either pointer accesses the same memory location, which means incrementing myNewPointer
will affect the value obtained by dereferencing the second_number
pointer.
Pointer Zero Values
A defined Pointer that is assigned the zero-value nil
by default.
In the preceding code, the second_number
pointer is defined but not initialized, it is passed as an argument to fmt.Println
function which prints out its value as nil
. In the subsequent statements, the address operator is used to create a pointer to the first_number
variable, and the value of second_number
is printed again.
N/B: A runtime error will occur if you follow a pointer that has not been assigned a value.
Summary
In this article, we learned about the basic types provided by Go, how constants and variables are defined, using both the full and short syntax; showed how to use untyped constants; and looked at a basic introduction to pointers in Go.