Golang Basic Types, Operators and Type Conversion

Go is a statically typed programming language. Every variable in Golang has an associated type.

Data types classify a related set of data. They define how the data is stored in memory, what are the possible values that a variable of a particular data type can hold, and the operations that can be done on them.

Golang has several built-in data types for representing common values like numbers, booleans, strings etc. In this article, We’ll look at all these basic data types one by one and understand how they work.

Numeric Types

Numeric types are used to represent numbers. They can be classified into Integers and Floating point types -

1. Integers

Integers are used to store whole numbers. Go has several built-in integer types of varying size for storing signed and unsigned integers -

Signed Integers

TypeSizeRange
int88 bits-128 to 127
int1616 bits-215 to 215 -1
int3232 bits-231 to 231 -1
int6464 bits-263 to 263 -1
intPlatform dependentPlatform dependent

The size of the generic int type is platform dependent. It is 32 bits wide on a 32-bit system and 64-bits wide on a 64-bit system.

Unsigned Integers

TypeSizeRange
uint88 bits0 to 255
uint1616 bits0 to 216 -1
uint3232 bits0 to 232 -1
uint6464 bits0 to 264 -1
uintPlatform dependentPlatform dependent

The size of uint type is platform dependent. It is 32 bits wide on a 32-bit system and 64-bits wide on a 64-bit system.

When you are working with integer values, you should always use the int data type unless you have a good reason to use the sized or unsigned integer types.

In Golang, you can declare octal numbers using prefix 0 and hexadecimal numbers using the prefix 0x or 0X. Following is a complete example of integer types -

package main
import "fmt"

func main() {
	var myInt8 int8 = 97

	/* 
	  When you don't declare any type explicitly, the type inferred is `int`
	  (The default type for integers)
	*/
	var myInt = 1200

	var myUint uint = 500

	var myHexNumber = 0xFF  // Use prefix '0x' or '0X' for declaring hexadecimal numbers
	var myOctalNumber = 034 // Use prefix '0' for declaring octal numbers

	fmt.Printf("%d, %d, %d, %#x, %#o\n", myInt8, myInt, myUint, myHexNumber, myOctalNumber)
}
# Output
97, 1200, 500, 0xff, 034

Integer Type aliases

Golang has two additional integer types called byte and rune that are aliases for uint8 and int32 data types respectively -

TypeAlias For
byteuint8
runeint32

In Go, the byte and rune data types are used to distinguish characters from integer values.

Golang doesn’t have a char data type. It uses byte and rune to represent character values. The byte data type represents ASCII characters and the rune data type represents a more broader set of Unicode characters that are encoded in UTF-8 format.

Characters are expressed in Golang by enclosing them in single quotes like this: 'A'.

The default type for character values is rune. That means, if you don’t declare a type explicitly when declaring a variable with a character value, then Go will infer the type as rune -

var firstLetter = 'A' // Type inferred as `rune` (Default type for character values)

You can create a byte variable by explicitly specifying the type -

var lastLetter byte = 'Z'

Both byte and rune data types are essentially integers. For example, a byte variable with value 'a' is converted to the integer 97.

Similarly, a rune variable with a unicode value '♥' is converted to the corresponding unicode codepoint U+2665, where U+ means unicode and the numbers are hexadecimal, which is essentially an integer.

package main
import "fmt"

func main() {
	var myByte byte = 'a'
	var myRune rune = '♥'

	fmt.Printf("%c = %d and %c = %U\n", myByte, myByte, myRune, myRune)
}
# Output
a = 97 and ♥ = U+2665

In the above example, I’ve printed the variable myByte in character and decimal format, and the variable myRune in character and Unicode format.

2. Floating Point Types

Floating point types are used to store numbers with a decimal component (ex - 1.24, 4.50000). Go has two floating point types - float32 and float64.

  • float32 occupies 32 bits in memory and stores values in single-precision floating point format.
  • float64 occupies 64 bits in memory and stores values in double-precision floating point format.

The default type for floating point values is float64. So when you initialize a floating point variable with an initial value without specifying a type explicitly, the compiler will infer the type as float64 -

var a = 9715.635   // Type inferred as `float64` (the default type for floating-point numbers)

Operations on Numeric Types

Go provides several operators for performing operations on numeric types -

  • Arithmetic Operators: +, -, *, /, %

  • Comparison Operators: ==, !=, <, >, <=, >=

  • Bitwise Operators: &, |, ^, <<, >>

  • Increment and Decrement Operators: ++, --

  • Assignment Operators: +=, -=, *=, /=, %=, <<=, >>=, &=, |=, ^=

Here is an example demonstrating some of the above operators -

package main
import (
	"fmt"
	"math"
)
func main() {
	var a, b = 4, 5
	var res1 = (a + b) * (a + b)/2  // Arithmetic operations

	a++ // Increment a by 1

	b += 10 // Increment b by 10

	var res2 = a ^ b // Bitwise XOR

	var r = 3.5
	var res3 = math.Pi * r * r  // Operations on floating-point type

	fmt.Printf("res1 : %v, res2 : %v, res3 : %v\n", res1, res2, res3)
}
# Output
res1 : 40, res2 : 10, res3 : 38.48451000647496

Booleans

Go provides a data type called bool to store boolean values. It can have two possible values - true and false.

var myBoolean = true
var anotherBoolean bool = false

Operations on Boolean Types

You can use the following operators on boolean types -

Logical Operators:

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

Equality and Inequality: ==, !=

The operators && and || follow short-circuiting rules. That means, in the expression E1 && E2, if E1 evaluates to false then E2 won’t be evaluated. Similarly, in the expression E1 || E2, if E1 evaluates to true then E2 won’t be evaluated.

Here is an example of Boolean types-

package main
import "fmt"

func main() {
	var truth = 3 <= 5
	var falsehood = 10 != 10

	// Short Circuiting
	var res1 = 10 > 20 && 5 == 5 // Second operand is not evaluated since first evaluates to false
	var res2 = 2*2 == 4 || 10%3 == 0 // Second operand is not evaluated since first evaluates to true

	fmt.Println(truth, falsehood, res1, res2)
}
# Output
true false false true

Complex Numbers

Complex numbers are one of the basic types in Golang. Go has two complex types of different sizes -

  • complex64: both real and imaginary parts are of float32 type.
  • complex128: both real and imaginary parts are of float64 type.

The default type for a complex number in golang is complex128. You can create a complex number like this -

var x = 5 + 7i  // Type inferred as `complex128`

Go also provides a built-in function named complex for creating complex numbers. If you’re creating a complex number with variables instead of literals, then you’ll need to use the complex function -

var a = 3.57
var b = 6.23

// var c = a + bi won't work. Create the complex number like this -
var c = complex(a, b)

Note that, both real and imaginary parts of the complex number must be of the same floating point type. If you try to create a complex number with different real and imaginary part types, then the compiler will throw an error -

var a float32 = 4.92
var b float64 = 7.38

/*
   The Following statement Won't compile. 
   (Both real and imaginary parts must be of the same floating-point type)
*/
var c = complex(a, b)  // Compiler Error

Operations on complex numbers

You can perform arithmetic operations like addition, subtraction, multiplication, and division on complex numbers -

package main
import "fmt"

func main() {
	var a = 3 + 5i
	var b = 2 + 4i

	var res1 = a + b
	var res2 = a - b
	var res3 = a * b
	var res4 = a / b

	fmt.Println(res1, res2, res3, res4)
}
# Output
(5+9i) (1+1i) (-14+22i) (1.3-0.1i)

Strings

In Go, a string is a sequence of bytes.

Strings in Golang are declared either using double quotes as in "Hello World" or back ticks as in `Hello World` .

// Normal String (Can not contain newlines, and can have escape characters like `\n`, `\t` etc)
var name = "Steve Jobs"

// Raw String (Can span multiple lines. Escape characters are not interpreted)
var bio = `Steve Jobs was an American entrepreneur and inventor.
           He was the CEO and co-founder of Apple Inc.`

Double-quoted strings cannot contain newlines and they can have escape characters like \n, \t etc. In double-quoted strings, a \n character is replaced with a newline, and a \t character is replaced with a tab space, and so on.

Strings enclosed within back ticks are raw strings. They can span multiple lines. Moreover, Escape characters don’t have any special meaning in raw strings.

package main
import "fmt"

func main() {
	var website = "\thttps://www.callicoder.com\t\n"

	var siteDescription = `\t\tCalliCoder is a programming blog where you can find
                           practical guides and tutorials on programming languages, 
                           web development, and desktop app development.\t\n`

    fmt.Println(website, siteDescription)
}
# Output
        https://www.callicoder.com      
 \t\tCalliCoder is a programming blog where you can find
                           practical guides and tutorials on programming languages, 
                           web development, and desktop app development.\t\n

That’s all about Strings in this article. But there is a lot more to learn about strings which include string indexing, handling unicode characters, performing various operations like string concatenation, split, join etc. We’ll learn about them in a future article.

Type Conversion

Golang has a strong type system. It doesn’t allow you to mix numeric types in an expression. For example, You cannot add an int variable to a float64 variable or even an int variable to an int64 variable. You cannot even perform an assignment between mixed types -

var a int64 = 4
var b int = a  // Compiler Error (Cannot use a (type in64) as type int in assignment)

var c int = 500

var result = a + c // Compiler Error (Invalid Operation: mismatched types int64 and int)

Unlike other statically typed languages like C, C++, and Java, Go doesn’t provide any implicit type conversion. To learn why Go is designed this way, check out the next article - Working with Constants in Golang.

All right! So we cannot add, subtract, compare or perform any kind of operation on two different types even if they are numeric. But what to do if we need to perform such operations?

Well, you’ll need to explicitly cast the variables to the target type -

var a int64 = 4
var b int = int(a)  // Explicit Type Conversion

var c float64 = 6.5

// Explicit Type Conversion
var result = float64(b) + c  // Works

The general syntax for converting a value v to a type T is T(v). Here are few more examples -

var myInt int = 65
var myUint uint = uint(myInt)
var myFloat float64 = float64(myInt)

Conclusion

That’s all folks! In this article, you learned about various basic data types provided by Golang, the operations that can be done on those types, and how to convert one type to another.

Thanks for reading. Stay tuned for more articles.

Next Article: Working with Constants in Golang

Code Samples: github.com/callicoder/golang-tutorials