Search
Close this search box.

Go: Core Concepts (Go Basics #2)

Constants, Variables, Data Types, Control Flow and Functions are most of the core concepts of Go programming language. After setting up your environment this will be where you start, while it is the first lesson, it’s one of the most important ones, everything you do going forward will build on top of these essentials, please take the time to fully understand the concepts, put in some practice and finally don’t forget to read Effective Go to get deeper understand of each of those concepts, links will be provided for relevant sections.

Data Types

Majority of the time working with Go application you are going to be actively using inferred data types when declaring variables, however it’s crucial that you get understanding of use cases and limitations of the individual types.

bool – boolean type used to indicate whether value is true or false, if unassigned the default value will be false


int & uint and their variations (int8, int16, int32, int64, uint8, uint16, uint32, uint64, uintptr) represent whole numbers in different ranges. Unsigned (so uint) will always have encompass same range of numbers as int, however they can go twice as high -1. For example int8 can be assigned numbers between -128 and 127, where is uint8 can be assigned only numbers 0 to 255.
Important to remember here is that when using the default type of int, uint or uintptr the byte size is platform dependent, meaning 32 bit system will default int to int32 (4 bytes) where as 64 bit will default to int64 (8 bytes) which is the respective limitation.

While building modern solutions it’s unlikely you’ll meet with 32bit systems it’s important to be mindful of the fact, and if developing for performance at scale, this is one of the considerations you’ll have to consider.

byte & rune are types that are alias for int8 and int32 respectively, with rune being used to represent unicode code points.

Go also provides two float types, float32 and float64 that respectively represent single and double precision floating numeric types that are platform independant.

Finally, there is complex64 and complex128 – while you should be aware of their existence, their use is very limited to niche professions and will not be covered in any of the future posts.

Operators

Operators are used to assign values or evaluate conditions, while there are many the most basic ones include:

= – the assignment operator
:= – assignment with type inference
== – equality comparison operator
>= – greater or equal
<= – less or equal
!= – not equal
&& – AND operator (connecting two conditions together and ensuring both are fulfilled)
|| – OR operator (connecting two conditions, and ensuring at least one is fulfilled)
Read more on operators from the language spec: https://go.dev/ref/spec

Constants

Constants & variables allow us to assign values. In case of constants value is assigned at compile time (that includes constants local to functions) and can be assigned only numbers, runes, strings or booleans – read more on this limitation at Effective Go.

Constants are assigned using the word const, you can perform one or multiple assignments at a time:

// type of string is inferred
const hello = "Hello, 世界"

// with explicit typing
const world string = "world"

as multiple assignment:

const hello, world = "Hello, 世界", "world"

// or:

const (
        hello = "Hello, 世界",
        world = "world"
)

Variables

Variables are very similar to constants in a sense they allow us to assign values, however unlike constants this assignment doesn’t happen at compile time but at runtime, which gives us the ability to assign functions to variables and other data types and also reassign the value at later time.

// type of string is inferred
var hello = "Hello, 世界"

// with explicit typing
var world string = "world"

// now let's reassign
hello = "Hello"

Control Flow

This is where you begin to solve real world problems by adding dynamic and responsive attributes to your programs.

If statement

If statement is the simplest form for evaluating condition, for example we could look at the variable we declared earlier called hello and using if statement determine that the content was in fact altered to just “Hello”.

To declare an if statement we start with the word if, followed by condition and curly braces that determine what is supposed to happen when condition is fulfilled. For the sake of this example we want to print out the word “Pass” – for that we will utilize fmt (format) package from the go library (packages will be covered in the following article)

// type of string is inferred
var hello = "Hello, 世界"

if hello == "Hello" {
    fmt.Println("Pass")
}
// because the variable hello is set to "Hello, 世界" at this time, nothing will be printed

// now let's reassign
hello = "Hello"

if hello == "Hello" {
    fmt.Println("Pass")
}

// the word "Pass" was printed as we have reassigned the value, which now has fulfilled the condition

It is common to see in Go that you use if statement alongside value assignment

// in first part before ; we attempt to assign err variable, then we check whether err is nil, if not we return the error  
// and stop executing any further code.
if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

While else block can be added, it is common practice to utilize if statements in a guard role, that is if we find an error in previous step, we stop any further execution by with either break, continue, return or goto keywords.

f, err := os.Open(name)
if err != nil {
    return err
}
// if err was found, function codeUsing() will never be executed as no file can be passed into the function
codeUsing(f)

For

For is used to loop over set of items, the most basic form of declaring for is for init; condition; post {} where init specifies initial value, for example i := 0, condition determines whether next iteration happens or loop ends (for example i < 10) and post performs an action after loop has ended, for example i++ to increment variable i.

While this is the most basic declaration, it’s rarely used in practice, the most common usage will be for iterating list of items stored in an array, slice, string, or a map, for this we use the range function.

for key, value := range items {
    fmt.Println(key, value)
}

// alternative declaration if you only care for the key or an index
for key := range items {
    fmt.Println(key)
}

// if you only care value use the blank identifier (underscore) _ 

for _, value := range items {
   fmt.Println(key)
}

Switch

Just like an if statement it allows us to evaluate whether value meets a condition, unlike an if statement it is more flexible and in cases where you need to validate against multiple values you will use switch to determine the proper behavior.

 switch {
    case severity == "warning"
        return "Pay attention!"
    case severity == "error"
        return "Something unexpected has happened!"
    case severity == "info"
        return ""
    }

Dive deeper into control flow with Effective Go.

Functions

Functions allow us to organize logic into reusable blocks, think how in our earlier example we check if variable hello is equivalent to the string “Hello”, instead of writing this twice we could have wrapped this logic into a function. Functions can take arguments, and return values, it is common for a function in Go to return to values, the actual return value and an error.

var hello = "Hello, 世界"

if hello == "Hello" {
    fmt.Println("Pass")
}

// now let's reassign
hello = "Hello"

if hello == "Hello" {
    fmt.Println("Pass")
}

// let's declare our function that will wrap logic for checking the variable hello
func isValid(variable string) boolean {
   // acts as a guard clause
   if variable == "Hello" {
       return true
   }
   return false
}

var hello = "Hello, 世界"

// pass variable into the isValid function
// variable passed is initialized
passed := isValid(hello)

// passed is false as condition isn't met
// TODO: verify it here by printing "Pass" if passed is true.


// now let's reassign
hello = "Hello"

// variable passed is assigned a new value
passed := isValid(hello)
// passed in this instance will be true

Deep dive into functions: https://go.dev/doc/effective_go#control-structures

Share the Post: