3 interesting primitive data types in Golang

Go is a statically typed programming language, which means once the variable is defined it’s data type cannot change. For folks working in dynamically typed languages like Python or Javascript this may seem a little tedious at first. But data types can help us think clearly about the variables, the operations we perform on these variable, the problems we’re solving and avoid mistakes with handling data.

golang primitive data types

TL;DR

Go supports multiple data types like many other languages do, but there are certain nuances specific to Go that we should know. In this blog we will cover three interesting data types specific to Golang as listed below.

  1. rune and byte
  2. error
  3. complex

We will also cover other primitive data types with code examples.

Named or Primitive Data Types

Most types in Go can be given a name that represents the type. Go, for instance, comes with a set of built-in primitive types that can be used to multiple types of data like textual, numeric, and boolean values. Let’s start with boolean data type:

Boolean

In Go, we use bool to define a variable as boolean type, and store boolean value like true or false in it. For a boolean type, false is considered to be the default value and the zero value of boolean type is also false. You can use the following operators on boolean types –

  • && (logical conjunction, “and”)
  • || (logical disjunction, “or”)
  • !   (logical negation)

Like in other languages, boolean operations also works on short circuiting principle. So in case of && operator the second operand is not even evaluated if the first operation is false (as 0 && 0 is 0 and 0 && 1 is also 0 so it really doesn’t matter if the second operator is not evaluated). And in case of || operator the second operator is not evaluated if the first operand is true. Here’s a code example of Boolean types in Golang:


package main
import "fmt"
func main() {
/* Assignment */
var isBool = true
var isActive bool //is_active is set to false
var isTrue = 1 <= 5 // as 1<=5 is true, isTrue variable is set to true
/* Short circuiting */
var res = 1 > 5 && 3 == 5 // First operands evaluates to false, so second is not evaluated
var out = 2*2 == 4 || 10%3 == 0 // Second operand is not evaluated as first is true
fmt.Println(isBool, isActive, isTrue, res, out)
}

Numeric types

Go supports both signed and unsigned integer types, here’s a list of allowed numeric types:

  1. int8, int16, int32, int64 — signed integers of 8,16,32,64 bit in size
  2. uint8, uint16, uint32, uint64 — unsigned integers of 8,16,32,64 bit in size

Unsigned integers only contain positive numbers (or zero). Both signed and unsigned integers can store values in certain ranges as listed in the below tables:

Signed Integers

Type Size Range
int8 8 bits -128 to 127
int16 16 bits -2^15 to 2^15 -1
int32 32 bits -2^31 to 2^31 -1
int64 64 bits -2^63 to 2^63 -1
int Platform dependent Platform dependent

Unsigned Integers

Type Size Range
uint8 8 bits 0 to 255
uint16 16 bits 0 to 2^16 -1
uint32 32 bits 0 to 2^32 -1
uint64 64 bits 0 to 2^64 -1
uint Platform dependent Platform dependent

There are also 3 machine dependent integer types: uint, int and uintptr:

  1. They are machine dependent because their size depends on the type of architecture
  2. Usually 32 or 64 bits wide based on whether it’s a 32bit or 64bit operating system
  3. Generally if you are working with integers you should just use the int type

Rune and byte

There are also some interesting numeric data type names in Go, let’s talk about them in detail:

  1. rune => it is an alias for int32
  2. byte => alias for uint8

Golang doesn’t have a char data type. It uses byte and rune to represent character values. So byte data types represent ASCII characters and rune data type stores Unicode characters in UTF-8 format.

  • ASCII defines 128 characters, identified by the code points 0–127. It covers English letters, Latin numbers, and a few other characters
  • Unicode, which is a superset of ASCII, defines a codespace of 1,114,112 code points

Characters in Golang are stored with single quotes, let’s understand with an example code:


package main
import "fmt"
func main() {
var myChar byte = 'A' //Stores ASCII characters
var myUnicode rune = '♥' //Stores Unicode characters
fmt.Printf("%c = %d and %c = %U\n", myChar, myChar, myUnicode, myUnicode)
}

Now if we print these values in different formats, you will see that

  1. myChar is converted in ASCII format, so it prints 65
  2. myUnicode is converted in Unicode code point which is U+2665

The rune type is an alias for int32, and is used to emphasize than an integer represents a Unicode code point. We will cover byte data type when we talk about String data types.

Floating Point

Like other languages, Go has support for Float types. We have two float types namely – float32 and float64, represented with 32 and 64-bit sizes in memory respectively.

An uninitialized float has a zero value of 0. Floating point values can be initialized with decimal point numbers or with exponents. While working with floating point numbers, the general recommendation is to stick with float64 data type.

Complex data type

And unlike other languages, Go supports complex numbers, complex128 and complex64. By now you must have guessed it, complex 64contains 32bit real and 32 bit imaginary part and complex128 has 64 bit real and imaginary numbers. Go also provides a built-in function named complex for creating complex numbers. You’ll need to use the complex function, if you’re creating a complex number with variables. Complex numbers allow all the basic mathematical operations: + , – , * , /

A few Go statements using float types are listed below:


package main
import "fmt"
func main() {
var (
pi float32 = 3.1416 //floating point number
z complex128 = 5 + 12i //complex number
)
/* creating a complex number */
var complx = complex(3, 4) //returns 3+4i
fmt.Println(pi, z, complx)
}

Strings

Strings in Go are declared in double quotes (“ ”) or backticks (` `). Here are a few examples of using strings:


package main
import "fmt"
func aStr() {
mstr1, mstr2 := "Hi", "Go!"
fmt.Printf("%s %s", mstr1, mstr2)
}
func main() {
var mystr string
var mstr = "Chetan"
fmt.Printf("%s %s", mystr, mstr)
aStr()
}

Strings can be accessed with indices like we do with arrays. So in this case, mystring[0] returns the first character in the string “Golang”, i.e.,  ‘G’

var mystring string = "Golang"
fmt.Printf("%s", string(mystring[0]))

Few things to note here:

  1. String indices are not of type string, instead they return data of type byte
  2. If we want to get the character, we should convert the byte to type string

In Python, strings are immutable, we see a similar behavior in Go. Try changing the first alphabet to something else:

mystring[0] = ‘H’

You will get an error cannot assign to mystring[0]. But if we really want to change the first character, we first have to convert the string to array of bytes, change the character and then convert it back to string

var mybytes = []byte(mystring)
mybytes[0] = 'H'
fmt.Printf("%s\n", string(mybytes))

We can also concatenate two strings in an intuitive way:

h := "Hello"
w := " World!"
fmt.Printf("%s\n", h+w)

Error

In Go you can communicate errors via an explicit return value. Errors are the last return value and have type error, a built-in interface. errors.New constructs a basic error value with the given error message. A nil value in the error position indicates that there was no error.

So you could handle errors in the code using the package errors, see below code example:

import "errors"

err := errors.New("Oops! something went wrong..")
if err != nil {
   fmt.Print(err)
}

It’s possible to use custom types as errors by implementing the Error() method on them.

Type Casting

Golang is a strongly typed language and doesn’t allow you to use multiple numeric types in an expression. So, we cannot add an int variable to a int64 variable or even perform an assignment between multiple data types. For example, if we try to assign value of a in b, the program will thrown an error.

var a int64 = 4
var b int = a  // Compiler Error

Type casting is a way to convert a variable from one data type to another data type. For example, if you want to store a long value into a simple integer then you can type cast long to int. The general syntax for converting a value v to a type T is T(v). So these statements would work:

var mInt int = 10
var mUint uint = uint(mInt)

Below is an example of type casting with division:

func cast() {
       var sum int = 27
       var count int = 10
       var mean float32
       mean = float32(sum)/float32(count)
       fmt.Printf("Value of division is: %f\n",mean)
}

In the above section on Strings in Go, while changing the string to bytes and other way round, we again used the concept of type casting.

Summarizing data types

Cool, so we covered a lot on primitive data types. Here are a few concepts that are important and you should keep note of:

Zero Value: Variables declared without an explicit initial value are assigned a default value. For instance following values are given to numeric, boolean and string types

  1. 0 for numeric types
  2. False for boolean types
  3. “” for empty strings

Explicit Conversions: Go assignment between items of different type requires an explicit conversion. For instance,

var f float64 = 8.00
var u uint = uint(f)

To make the value unsigned int from float, we need to explicitly convert it with the appropriate data type as shown in the code example

Type Inference: When declaring a variable with either := or var = syntax, the data type of the variable is decided based on the value on the right hand side. For instance in the following code snippet, a is of type int and c is of type complex:

a := 42
c := 2 + 3i

Great, so in this blog we understood the basic data types in Go with code examples. Complex and error types were interesting, right? And how about rune and byte, equally mind boggling!

Let’s continue our journey with Go when we meet next. Ta da for now 🙂

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.