Golang: Variables and Types

Golang: Variables and Types

Introduction

In the third part of the series, we will be covering the fundamentals for learning any programming language i.e. variables and data types. We will be covering from data types to variable declaration. We won't be seeing each and every detail related to the data types as some of them require a knowledge of loops and other topics, so that can be left for the different part.

Types in golang

In Golang there are 3 major types : Numeric, Bool and String. Further we also have specific types for the three data types like int, float, rune, byte, etc. We will first see how to declare a simple variable and then explore the data types in Golang.

var name string

This the variable declaration in Golang, we have the keyword var similar to Javascript but we optionally have to specify the type of the variable if we are not immediately assigning/defining it a value.

To assign variable values, we can write the datatype of the assigned value but it is optional as the go compiler will know the datatype according to the assigned value. Further you cannot change the type of that variable once it is initialized.

var name string = 'Gopher'

OR 

var name string
name = 'Gopher'

OR

var name = 'Gopher'

We also have const for assigning constant values to a particular value set. You cannot change the value for a constant type, doing this will result in compile time error. An important property of const can be noted here, if you simply declare a const without initializing the value and try to access that constant, the go-compiler will throw an compilation error.

const name string = 'David'

OR

const name string
name = 'Calvin'

OR

const name = 'Smith'

By default, the values for string is an empty string'', for integer and float it is 0 and for bool it is false.

Each of these are valid declaration of variables in golang. Let's now dive into the data types and follow up with variable declaration in detail later.

NumericStringBool
intstringbool
float
complex
rune
byte

Numeric

Let's first explore the numeric data types in golang as you have guessed correctly, we have int and float as distinct categories but further we also have fine grained storage types for both of the types.

Integer

In integers as well we have two categories signed and unsigned, we can basically store only positive integers in unsigned integers giving us an extra bit to play with.

For Integers, we have specific data storage types depending on the bits it can store like int8 for storing 8 bit integers, int16 for storing 16 bit integer value, int32 and int64 for the given number of bits in the integer. Similarly we will have these storage based integer values for unsigned integers like uint8, uint16, uint32 and uint64. We can basically store double amount of positive integers in unsigned integers as uint than in signed integers int, this is because the most significant bit is not used as a sign bit since all values in the variable are positive and hence no sign bit is required.

var likes int = 140
fmt.Println(likes)
$ go run int.go
140
var age int8 = 140
fmt.Println('Age = ',age)
$ go run int.go
# command-line-arguments
int.go:6:9: constant 140 overflows int8

This will give us an error as 140 is above the limit for int8. So, unless you have specific requirements as storage limitation, you should be using int as the default data type for storing integers.

So, we need to define variables as per the limits to which we are going to use them, if you just specify int the type will be selected based on your operating system, if it is 32bit, it will take int32, for 64bit OSes it will take as int64 integer. If you define a variable with let say 16 bit storage and if it exceeds the limit for 16 bit storage, it would give a overflow limit error.

Below are the limits for all the integer types in Golang:

uint8 ->  0  to  255
uint16 ->  0  to  65535
uint32 ->  0  to  4294967295
uint64 ->  0  to  18446744073709551615

int8 ->  -128  to  127
int16 ->  -32768  to  32767
int32 ->  -2147483648  to  2147483647
int64 ->  -9223372036854775808  to  9223372036854775807

If you want to reality check for the boundary values of this data types, you can code a program in go as below:

  • To find the maximum value of uint, we can flip all the bits in 0 to get all the set bits in the integer thus we use ^ operator.
  • To find the maximum value for signed integers, we can right shit one bit so as to unset the sign bit.
  • The minimum value for uint is the default value 0.
  • The minimum value for int can be calculated by subtracting one from the negative of the max limit.
package main

import (
    'fmt'
)

func main() {
    var min_uint = 0
    var max_uint8 uint8 = ^uint8(0)
    var max_uint16 uint16 = ^uint16(0)
    var max_uint32 uint32 = ^uint32(0)
    var max_uint64 uint64 = ^uint64(0)

    var max_int8 int8 = int8(max_uint8>>1)
    var min_int8 int8 = -max_int8 - 1
    var max_int16 int16 = int16(max_uint16>>1)
    var min_int16 int16 = -max_int16 - 1
    var max_int32 int32 = int32(max_uint32>>1)
    var min_int32 int32 = -max_int32 - 1
    var max_int64 int64 = int64(max_uint64>>1)
    var min_int64 int64 = -max_int64 - 1

    fmt.Println('uint8 -> ', min_uint, ' to ', max_uint8)
    fmt.Println('uint16 -> ', min_uint, ' to ', max_uint16)
    fmt.Println('uint32 -> ', min_uint, ' to ', max_uint32)
    fmt.Println('uint64 -> ', min_uint, ' to ', max_uint64)
    fmt.Println('')
    fmt.Println('int8 -> ', min_int8, ' to ', max_int8)
    fmt.Println('int16 -> ', min_int16, ' to ', max_int16)
    fmt.Println('int32 -> ', min_int32, ' to ', max_int32)
    fmt.Println('int64 -> ', min_int64, ' to ', max_int64)
}

This was the basic overview of Integers in Golang.

Though rune and byte are Integer aliases, we will explore them in the String section as they make a lot of sense over there.

Float

Similar to integers, we also have floats in the numeric category. A float is a numeric data type that can allow numbers with decimal values. A simple float can be of either float32 or float64. The two types are defined as the precision of the decimal values. Obliviously, the float64 type is more precise than the counterpart and is also the default type assigned if not provided.

const percent = 30.5
fmt.Println(percent+50)

You optionally provide the float32 type to have a bit less precision than usual using the float32 keyword as follows:

const percent float32 = 46.34
fmt.Println(percent - 3.555)

The floating value precision of the float types in golang are as follows:

float32      -->   -3.4e+38 to 3.4e+38.
float64      -->   -1.7e+308 to +1.7e+308.

As quite logical reasons, the precision is almost double in the float64 compared to float32. If we try to add(any operation) a float64 number with a flaot32, we get an error as performing operations on two differently stored types can't be operated.

Complex Numbers

We also have complex numbers in golang. This are the numbers which deal with a real and a imaginary numbers. We initialize the complex variable using the complex function which takes two parameters the real part and the imagianry part. Both the parts or numbers are stored as float in the complex data type.

So, since golang has float32 and float64 data types, we will have complex64 and complex128 as complex types. Since we are storing two flaot64, it has a complex128 type and complex64 for both parts as float32. Here as well, you cannot store the two parts(real and imaginary) as different float types i.e. You need to have both real and imaginary as either flaot32 or flaot64.

var percent = 30.738
var f = 4.545
var comp1 = complex(f, percent)
var comp2 = complex(percent, f)
fmt.Println(comp1 - comp2)
(-26.192999999999998+26.192999999999998i)

Golang automatically adds the i or iota in the complex/imaginary part for better readability.

Strings

We can now move onto the string data type in golang. It has several data types like byte, rune, string. In golang, byte and rune store individual characters whereas string can hold multiple characters.

Byte

A byte in golang is an unsigned 8 bit integer, which means it can hold numeric data from 0 to 255. So how is this displaying characters if it stores integer. Well, because each number it stores is mapped to the ASCII character set which is used to represent characters.

A byte can be stored in a single quote '', if we use double quotes'', the variable is considered as string if we aren't specifying the data type.

const c byte = 't'
fmt.Println(c)
$ go run byte.go
116

This gives the output as a number between 0 and 255 depending on the character which you have stored. To print the actual character you need to type caste into a string like:

const c byte = 't'
fmt.Printf('Character = %c 
Integer value = %d
', c, c)
$ go run byte.go
Character = t
Integer Value = 116

We can get the character equivalent of the byte representation number using the Printf function and the %c place holder for a character. The is used to end the line just for displaying proper output.

We can even store numbers between 0 and 255 as it is internally an uint8.

Rune

A rune is extended type of byte as it stores int32 numbers and hence it represents Unicode characters. This is the default type if you do not specify byte and use single quotes to assign a character. Using rune, we can assign it an unicode characters to display some rich characters and literals like emoji or expressions.

var smiley_emoji = 'u263A'
fmt.Printf('Smiley Emoji --> %c', smiley_emoji)

GO Rune Smiley Emoji

So, rune is pretty amazing type to play with characters in golang. As it is a default type assigned against byte if not provided while assignment.

String

Strings are basically a slice(list) of bytes. Each character in a string is a byte. By default the string will be empty if you don't initialize it with a value.

const language string
language = 'C++'

OR

const language string= 'Python'

OR

const language = 'Javascript'

We can even access particular character in the string using it's index.

const code = '12AB34CD'
fmt.Println(code[6])
$ go run string.go
67

This gives us a integer as we are accessing the byte from the string using its index. Thus, we can use %c in the Printf function to format and print the equivalent characters of the byte.

const code = '12AB34CD'
fmt.Printf('2nd Character in string = %c
', code[4])
$ go run string.go
2nd Character in string = A

We can also declare strings using backticks/backquotes or whatever you call it (```), assigning string with this allows us to write multi line string.

var statement = `This is the first line
The next line
The last line`

fmt.Println(statement)
$ go run str-backticks.go
This is the first line
The next line
The last line

Further in the loop article we will see how to loop/iterate over a string.

Boolean

This type is used to store either true or false in golang. The default value of a boolean variable is false.

var power bool
fmt.Println(power)
$ go run bool.go
false

We can assign the variable as either true or false.

const result = true
fmt.Printf('The statement is %t', result)
$ go run bool.go
The statement is true

So, using the %t we can print the value of a boolean value in golang in the Printf function.

Creating Variables

Now, we have familiar with data types in golang, we can more expressively create, declare, initialize variables in golang.

There are 3-4 primary ways to define a variable most of which we have already seen.

Declaring a Variable

We can declare a variable without assigning it any value but for that we need to then provide the data type, this can be done using the following command:

var expereience int

expereience = 2

We can even use const for constant value in the given scope.

Here, we can even declare multiple variables by separating each variable/constant with comma , which can be done as follows:

var a, b, c int

OR

const i, j, k bool

Defining and Initializing at the same time

We can initialize a variable/constant in golang by explicitly giving it a value. We can do that by using var for variable value or const for a constant value. We can optionally provide the data type at this moment as golang will automatically detect the type and assign the memory according to the value given.

var place string = 'home'

Here, there is no compulsion to provide the datatype as the compiler will be able to know it from the asisgned value. Though if you want to provide a non-default value you can specify the datatype.

Declaring Multiple Variables

We can assign multiple variables at once by separating them with comma,. The variable name to the left and the values to the right needs to separated with comm on both sides.

var x, y, z = 100, '#', 'days'

fmt.Printf(' x = %d 
 y = %c 
 z = %s 
',x,y,z)
$ go run multiplvar.go
 x = 100
 y = #
 z = daysofcode

We can are declaring and assigning multiple variables, the x variable is assigned an integer value, y with a rune(by default) and z with a string. We are using Printf function with place holders for int %d, rune/byte %c and string as %s. The is for a new line.

If we want to assign the variables with a particular data type, we can use the var keyword as a list of values.

var (
    x int8 = 100
    y byte = '#'
    z =  'daysofcode'
)

fmt.Printf(' x = %T 
 y = %T 
 z = %T 
',x,y,z)
$ go run multiplvar.go
 x = int8
 y = uint8
 z = string

This is not only limited to var we can also use const to declare multiple constants with type constraint. Also, note we are using the %T placeholder for getting the type of the data stored in the variable.

Also, we can define(declare and initialize) multiple variable with same data type with command separated as follows:

var pi, e, G float32 = 3.141, 2.718, 6.67e-11   
var start, end byte = 65, 90
fmt.Println(pi, e, G)
fmt.Printf('%c %c
',start, end)
$ go run multp.go
3.141 2.718 6.67e-11
A Z

Assigning Variable using Walrus Operator (Shorthand Declaration)

We can skip usign var or the datatype by using the := walrus operator. This type of assignment using walruns operator can only be allowed in the function body and not anywhere else, in the global scope this type of assignment is not allowed.

place := 'school'

This is such a simple shorthand for assigning variables though only in a function body.

Also, multiple variable declaration is possible with walrus operator.

x, y, z := 'foo', 32, true
fmt.Println(x, y, z)
fmt.Printf('%T %T %T', x, y, z)
$ go run walrus.go
foo 32 true
string int bool

Links to all code and links are visible on the GitHub repository.

Conclusion

So, from this part of the series, we were able to understand variables and the various data types in Golang. Though we didn't got too much in detail still we can find ourselves a bit comfortable in understanding basic go scripts. In the next section, we will looking into conditional statements and loops. This would give a good grasp on iterating over a string and even learn arrays(just the basics) we will explore Arrays and slices(remember strings?) after that.

So, if you have any questions, suggestions, or feedback please let me know in the comments or on the social handles. See you next time, Happy Coding :)