A Succinct  Introduction to Types, Values, and Pointers in Go

A Succinct Introduction to Types, Values, and Pointers in Go

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.

TypeExamples
intThere 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.
uintNo uint literals. All literal whole numbers are treated as int values.
byteNo byte literals.
float6420.2, -20.2, 1.2e10, 1.2e-10. Values can also be expressed in hex notation(0x2p10).
booltrue, 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 'priceandtaxvariables and assignedfloat32values to them. Then a new value is assigned to thepricevariable via Go's assignment operator(Notice that we assigned the value500to a floating point variable. This is possible because the *literal* value500is an untyped constant that can be represented as afloat32` 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.