DEV Community

Cover image for The Simple Golang Tutorial Part 2 - Variables, Types, and Operators
Ali Sherief
Ali Sherief

Posted on

The Simple Golang Tutorial Part 2 - Variables, Types, and Operators

#go

Welcome back to the Golang series. Let's try to get a good understanding of how to declare and use variables, working with the types available and the operators you can use on variables in Golang.

Variables

We will start with variables. Recall that in the previous article, we saw that the := operator both declares and assigns the variable a value. Well, did you know that it is possible to split these operations into two by declaring a variable by itself and also performing the assignment in another statement?

To declare a variable, all we have to do is prefix it with var:

var foo
foo = 3
Enter fullscreen mode Exit fullscreen mode

We can also see that assignment here is, as in many other languages, done with the = operator. We will see more about this in the operators section.

Notice how there is no semicolon on either of these two lines. In Go, the semicolon syntax construct exists, but it can be omitted most of the time.

So that's it for declaring variables. Let's move on to the next important subject: types.

Types

If you look carefully at the code snippet above, you can see that we assign a number to foo. Indeed, the "3" is an integer type, one of several types available in Golang.

A non-exhaustive list of basic types are enumerated below:

Several signed and unsigned integer types

Signed is just a fancy way of saying that the number can represent negative integers. An unsigned type uses the bits usually used to store the sign bit to store more bits of positive numbers. This means unsigned types have a higher range of positive numbers but cannot represent negative numbers.

Golang has two generic signed and unsigned integer types: uint is the unsigned integer, and int is the signed integer type. The Golang compiler determines these variables' size, but usually, they are 32 or 64 bits long.

There are also fixed-width integer types that have the same size no matter what the compiler does:

  • int8 and uint8 are 8 bits wide
  • int16 and uint16 are 16 bits wide
  • int32 and uint32 are 32 bits wide
  • int64 and uint64 are 64 bits wide

Also, byte is an alias for uint8, and rune is an alias for int32.

Floating-point and complex types

Golang also has a single-precision float type 32 bits long that can store decimal numbers and a larger 64-bit double-precision float that can hold more decimal places of number and has a much larger range than single-precision floats. Their names are as follows:

  • float32 is the 32-bit single-precision floating-point type
  • float64 is the 64-bit double-precision floating-point type

Unlike many other languages, there is a complex number type built-in. complex64 has float32 real and imaginary parts, and complex128 has float64 real and imaginary parts.

Pointers

Simply put, you can make any type a pointer by putting a * on the left of the type name. So int becomes *int, float64 becomes *float64, and so on.

There is even a pointer type called uintptr that doesn't actually act like a pointer, but it lets you get the bits of the pointer directly.

Arrays

Arrays are declared differently than in most other languages. The brackets and size are to the left of the array name rather than on the right.

The consequence of placing it this way is that types are evaluated from left to right (instead of array braces having special precedence for some reason), and this makes it easier for you to understand the array's type:

*int  /* pointer to int */
*[4]int /* pointer to an array with 4 int elements */
[4]*int /* array with 4 int pointer elements */
Enter fullscreen mode Exit fullscreen mode

Much easier to read than int* a[4] or int (* a)[4], right? (Unless you are used to C++ like I am).

Structs

We can declare any structure using curly braces and the struct keyword like this:

struct {
    x, y int
    z float32
}
Enter fullscreen mode Exit fullscreen mode

This is a structure with two ints called x and y, and one float32 called z. We can also use pointers and arrays in a struct.

Also, we can put [1234567890] or * at the left of the struct to make it an array of structs or a pointer to a struct, respectively.

Operators

We have essentially the same set of arithmetic and logical operators that other languages have, but for the sake of completeness, let's round them up here.

We have addition +, subtraction -, multiplication *, and division /. We also have modulus % (the remainder of divisions), equality and inequality == and != respectively, less-than < and greater-than <, and hybrid combinations of these <= and >= for less than or equal and greater than or equal respectively.

We have logical AND and OR && and || which help chain conditions (we do not have a logical NOT operator), and finally, there are the bitwise operators: logical AND, OR and XOR &, | and ^ respectively, left and right shift << and >>, and a new bitwise operator called AND NOT &^. There is no bitwise NOT operator that inverts all the bits like C's ~ operator.

At this point, it will be helpful to know the operator precedence or the order in which Golang evaluates operators. Here's the list copied from its reference manual:

Precedence    Operator
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||
Enter fullscreen mode Exit fullscreen mode

Be careful with this because the precedence is very different from other languages such as C/C++/Java and PHP. In particular, all the relational operators have the same priority, bitwise OR and XOR have the same precedence as addition and subtraction, and bitwise AND and shifts have the same precedence as multiply and divide.


That's all for this week, folks. Next week we'll be looking at functions, some relevant function statements, and some built-in functions.

Top comments (0)