Theory
Intro
Go has a different syntax and is kinda confusing for some people to get around so this module will be discussing theory within the Go programming language.
What is programatic theory?
When it comes to programming, theory, is the study of concepts within a program or in this case specific programming language. Learning theory will help you learn the logical pathways for the language and understand how to piece new concepts together.
What will this module contain?
This this is not CompSCI, Programatic, General or computational theory and rather a field that focuses on the prime aspects; This module will contain information about the Go programming language such as a file layout, module layout, type system, working with different structures, working with functions and so on from there.
File Contruction
When it comes to working with Go, there are many missed aspects within the language, such as the file construction. Within golang, there are is multiple fields to talk about, the top level, mid level and bottom level of the program. Typically, the mid of the file is the secondary "top" level of the program. What defines these?
Top Level
The top level of a go program or .go source code file MUST contain a package name. To declare a package name you type package
followed by the name of the package or module you are making. For the basic sense of things, a simple go program that is a main file or main entry to a program, must contain package main
this is also where the main
entry point is to the program.
This is pretty simple to understand as the first and primary top level of the program or first line of every go program MUST contain a package name.
Secondary Top Level AKA Mid level.
For programs that use libraries, Golang only allows you to import or link modules using the import keywords for which is defined at the top of a program. Say we want to write " hey guys \n hows it going! " in a program using the FMT package. We have to write this in the following form
This is pretty easy to dissect and look at, what about listed imports? Cool thing about go is that importing files or even libraries or modules can all be done simply by using a list within the import expression which is shown below.
This program prints out the arguments when the program is run or rather the first command line argument. As you can see, the import list does not require comma's as it relies on whitespace.
Bottom Level
The bottom level of a program is where all your functions are typically going to be stored or executed calls. In the case of our program above, the main function is at the bottom level or scope of the program.
Executing Files
Basic execution
Executing golang source code files can be quite easy, but golang has quite the tools. To simply run a golang file such as the one shown below
you type go run main.go
. Remember that the main file must have package main
in order for it to be run and have a valid entry point declared as main
for the program to run.
Building to a executeable
In order to build a golang program for the current architecture or system you are on, simply run go build
. When this is built if no output flag is defined, golang will then take the file name and compile it to have the executeable name set as the file names. So if we compiled hello.go
without a specification on the name then the output will be hello.exe
on windows or hello
on linux.
Naming builds
Golang has many variables and flags you can use to optimize the build's name and target.
Use the -o
option followed by a name to compile by name like so; go build -o HelloProgram main.go
Using Env variables to customize builds and targets
In the previous section I had mentioned environment variables and types for the Go programming language. Well, Go has the following environment variables.
Environment Variable | Description | Example |
---|---|---|
| Specifies directories to search for executable programs |
|
| Specifies the user's home directory |
|
| Specifies the username of the current user |
|
| Specifies the Go workspace directory |
|
| Specifies the directory where the Go installation is located |
|
| Specifies the target operating system for the build |
|
| Specifies the target architecture for the build |
|
| Specifies the directory where Go binaries should be installed |
|
| Specifies the path to a file that overrides environment vars |
|
In order to use these environment variables we can change their values before build such as
GOOS=linux go build -o hello main.go
which will build the file specific towards the ELF file format.
Looking for environmental support
Sometimes when working with Go and compiling programs in Go, you may want to compile for another architecture and system. To do so, execute the following command to drop a list of all architectures and operating systems supported.
go tool dist list
which will drop down the entire list of possible targets allows by the compiler.
We can now make our program build for AMD64 and Linux by issuing the following command
Functions
Before we get into the data types of Golang we want to first be able to understand how functions work. This module will be aimed at giving you a base introduction to functions and then an intermediate example of those functions.
Functions | Basic Function Decl
In order to declare functions in Golang there are multiple ways you can initalize them and even declare them, but the main one is the func
keyword which stands for function
similar to fn
in rust.
Below is an example of a very basic function and how you can call them.
This function is declared and takes no arguments, returns nothing and has no structure tag as it simply just prints hello world to the console. Now, if we wanted to go bigger we could. Lets look at the anatomy of a function in Go.
Functions | Anatomy of a function
A function has an few interesting fields but the prime exist of all optional sets.
Still confusing? Lets break this down then.
Function Decl: We established that declaring a function in go requires the func
keyword
Function Type Tags: In golang, you can use type alliases or structures to call a function ( explanation on this later ). This is defined with () before the function name
Function Name: Function name comes after the func
keywords if structure tags or type tags are not within the function
Function Params / Arguments: Functions in golang can have arguments and you declare this by delcaring the function name then a set of parenthesis
Function Returns: Function return types in go can be declared multiple ways. If you want to declare a list of return types then you have to use () which is the list of types like (string, int, string)
or you can just declare the type after the function parameter decl.
Function Block: Function blocks are called and defined with right and left facing brackets. Right facing brackets define the functions start and left facing brackets define the end of a function.
Functions | Arguments
A function as shown above can have multiple arguments, but how do you define them?
Functions have a few rules when types are used as arguments which are the following.
Rule | Description | Example |
---|---|---|
Any Type | Functions can hold ANY data type | func setting(a string, b map[string]string, c []byte, d interface{}){} |
Main can not hold function arguments | any | main(data string) is ILLEGAL |
params must be named | for each argument within a function it must be named | func setting(age int, name string) |
Arguments as you can see are quite simple and basic despite having some rules, lets write a program demoing how function arguments can be used
When this program outputs we get hello there ryan
because we used the string argument "ryan" as a replacement for name and concentrated the strings together. Note that arguments can also be defined in many different ways as well such as infinite arguments otherwise known as vardaic functions.
Lets build a program that allows you to output one giant line of concentrated strings
Running this code will produce a output that will concentrate all the names provided in the argument list. Note that infinite arguments MUST ALWAYS BE DEFINED AT THE END. So for example the function func hi(name ...string, age int)
will throw the error can only use ... with final parameter in list, COMPILER ERROR > MisplacedDotDotDot
Functions | Multiple variables at once in arguments
Functions can also use multiple variables all of one type within functions like the following.
both name
and rank
all have to be string types when the function is called, but instead of doing name string, rank string, player string, user string
you can concentrate that into a list of variables with one assigned type.
Functions | Returns
Returns are quite simple, the most basic return of a function happens outside of its parameter list and is defined by the data type you want to return. The function below returns an integer data type after adding two parameters.
The function takes not only two input params of type integer but then returns the result by adding them together. In our main function we just declare a variable to hold the result of that function and then print it to the console.
Functions | Returns (Variable and List based)
Functions can also return multiple types and even declare variables like the following function below which returns two variables of the same type
When you return variables within a function, you do not need to return direct values or variables as using the return
keyword will just return the variables. Looking back at the anatomy, the secodn set of parenthesis is the listed or variable return set. We can also just return multiple types without having variables like so.
Notice that this time we have to declare the variables with the decl operator :=
and specify them within the return call. If you are working with more bigger functions that return larger sets of data, declaring a list of variables and types may become much more helpful.
Functions | Anonymous functions
Functions in Go can be defined using the func() keyword, and we call them typically by the name of the function. But what about anonymous functions? How do those work? What can we do with them?
Anonymous functions are functions that have no name and are called directly. This means we define func() and then call it. A program below defines the use of this.
This is an example of what a anonymous function looks like. The function defined is Add
which takes two parameters of type int, it then formats the output of the calculation. Lets break this section down line by line.
In this part of the brick all we are doing is a simple x + y
operation where we add the input values and we print it to the screen. We do this by making an operation called Sprint
which stands for String
print to convert any data type to a string. We then concentrate the result of Sprint(x)
with +
to format the expression, then add the sprint of y, then an assignment operator and the final result being the operation that was sprinted. So out final message should look like 12 + 20 = 32
This brick calls the anonymous function, anonymous functions have a way of calling and returning data types but in this case we just call it with arguments ot simulate a more advanced scenario. Generally speaking we call an anonymous function by using func(){}()
where the first set of ()
is the param list the function takes and the second set of ()
is the argument input. We then do the same exact operation that we did before this block of code just for division.
Now lets look at a much more basic function that just outputs a hello world statement.
ah yes this is more readable and should give you a good understanding of what exactly a anonymous function looks like in go.
Data Type System
Golang has a very strong type system that allows you to do so much such as creating your own type structures but before we get into that we should define all of the data types and get a simple understanding of every data type.
Data Type | Description | Example | Use Case |
---|---|---|---|
| Boolean type representing true or false values |
| Storing and manipulating logical conditions |
| Signed integer type |
| Representing whole numbers |
| 8-bit signed integer type |
| Optimizing memory usage for smaller integer values |
| 16-bit signed integer type |
| Handling larger integer values |
| 32-bit signed integer type |
| Interacting with system calls, networking, and file systems |
| 64-bit signed integer type |
| Dealing with very large integer values |
| Unsigned integer type |
| Representing positive whole numbers |
| 8-bit unsigned integer type |
| Manipulating bytes and color values |
| 16-bit unsigned integer type |
| Managing network protocols and encoding |
| 32-bit unsigned integer type |
| Working with large sets of unique identifiers |
| 64-bit unsigned integer type |
| Handling large numbers or bit manipulation |
| Single-precision floating-point type |
| Representing and manipulating decimal numbers with less precision |
| Double-precision floating-point type |
| Handling decimal numbers with higher precision |
| Sequence of characters |
| Storing and manipulating text |
| Fixed-size collection of elements |
| Working with a fixed number of related values |
| Unicode code point |
| Working with individual characters and Unicode strings |
| Alias for |
| Handling binary data, I/O operations, and network protocols |
All of these data types seem quite confusing but this can be quite easy to understand when we get a short hang of everything.
Arrays
Arrays are a very powerful data type within Go and can be used for many different things. Arrays are weird syntactically within Go but can be easy to remember. The following sub list talks about different arrays and their use case.
String Arrays
String arrays can be defined with []string
. Generally speaking, all arrays within the go programming language are declared with []<type>
where <type>
is replaced with the data type from the table above.
String arrays look like the following.
Float Arrays
Float arrays follow the same concept
Integer Arrays
Like all arrays integer arrays are the same
The interface data type
The interface data type is the any
data type of the Go programming language. This means that this data type can be anything and can be one data type of any type within the type list in Go. Interfaces are defined with interface{}
and have their own unique fields. Below is an example of a interface array.
Notice how the array does not have any one specific type? That is how an interface works, it is any possible data type.
The rune data type
Runes are another confusing factor with people, for some reason it can be confusing to chomp down so lets break it down. A rune is golang's way of declaring unicdoe point. The following example prints out 65
.
This program outputs the value of r, but if r is a character then why is it outputting 65
? This is because A as a unicode point holds the 65th position on the ASCII table shown below.
As you can see rune is just another way to represent unicode point. Note that the rune type can only be used for single characters and can not be more than one character or one unicode point.
The byte type
The byte type witin the programming language is easy to understand and does not have a weird understanding behind it. Bytes come in two forms which include a standard byte and a range of bytes ( array of bytes ). Below is an example of both
As you can see the byte type can be used for many things and if we print the h character in a byte format we get 104
similar to when we work with rune. Using byte's can be super benifical when reading files, working with networks, dissecting packets or trying to parse binary data and files.
Type
The type system we just went over but did not fully cover, one thing we missed was the type and struct keywords. In Golang, you can create your own data types using the type keyword and also create your own data type structures ussing the struct keyword. There are multiple ways to work with this but lets first break down the type keyword.
type
basic usage | Type Aliases
The type keyword allows you to create alliases to other data types which can be used to shorten the lenght of specific functions, uses, cases or even arguments. Below is an example
As you can see we made UserID of type int which means we can now use it and call it as a type.
type
different usage | New Data Types
Creating new data types can also be helpful especially when you want to work with functions or even hold sets of data. In this case we create a Celsius and Fahrenheit data type which are both float64 values.
We declare our types using the type keyword, their name followed by the type and then define a function that requires the Celsius type to be called which causes a function to output the Fahrenheit data type. This type of usage for types really comes in handy when we get to better and more detailed operations. It is quite simple to read the code if you are not directly new to go but can be confusing for new comers so lets break it down one last time.
Types: We declare the types we want to use using the type
keyword we then define the data type that new data type we are creating is holding.
Function Call: The function in this program converts celsius to fahrenheit and in order to do that the function needs to use the Celsius data type. After the func
keyword is defined, we place a set of parenthesis which holds the variable we will use to call the structure and then the structure name. We then declare the function name and what it is returning and return the Fahrenheit conversion algorithm with the variable c
which again is the Celsius type which holds a float64 data type.
Main: Our main function is the main entry point that will call the Celsius type to iniate a new value for cel
, we then call f
the output to the function by calling cel.ToFahrenheit()
.
Struct
Structures are an amazing composite data type that allows you to define your own custom data structure. This can become helpful when building programs in Golang because it allows for developers to easily define custom data types and organize related data.
Struct
| Theory
Structures can be easily declared with the type and struct keywords! The anatomy of the structure is the following
so putting a structure together we have
Struct
| Basic Usage
Using a structure for most people is quite easy when you get the hang of it! The following program uses a structure to store a username and password
As you can see, this program first starts off by declaring the structure and two variables; Name of type String
and Password of type Integer
. When the structure is declared we then see the var Conf Config
which may be easy to pick apart but in case not, the Conf
variable holds the Struct Config
as a value. This is the variable we will use to globally work with the structure. We then create a pre entry point in the program where we can assign the structure variables and then the main function will print out the details.
Struct
| Initializing Structs
When we do not want to using variables, we can initialize structures using basic variables like so.
Sometimes this can come in handy when we want to use structures under specific functions instead of just globally initializing them!
Struct
| Anonymous structures
Anonymous structures are quite easy to understand as they provide much more flexibility for their use case and work similar to anonymous functions!
As you can see by the initializing it can go quite the long way when you need a structure quick and easy for private use!
Struct
| Pointer to Struct
Sometimes, a function may require a structure as an argument and in order to do that we need to be sure that we can work with different structures! A program below takes a structure as a pointer and assigns new values to that structure.
In this program, we define a Person
struct with Name
and Age
fields. The UpdateUsers
function takes a pointer to a Person
struct as an argument, along with new values for the name and age. Inside the function, we use the pointer to update the values of the structure fields. Then in the main
function, we create a new instance of Person
using the address-of operator (&
) (The address-of operator in Go is represented by the & symbol. It is used to obtain the memory address of a variable or a value. ). We then print the initial values of the structure fields. Next, we call the updatePerson
function, passing the pointer to the Person
instance and new values. The function modifies the structure fields using the pointer. Finally, we print the updated values of the structure fields.
Struct
| Structure Composition
When working with structs in Go, you may want to use one structure that holds a variable with the value of another structure. This looks like the following.
As you can see we create multiple variables that hold the structurs storing the data we need it to store, notice how when we get to the Address
field in the variable person
we fill the Address
field with the address
variable that holds a structure and its values apart of the Address
.
Struct
| Structure Tags
Another use for structures is to take forms of data such as JSON, XML, YAML and using it to store those values which can be marshaled into the data structure.
This is quite easy to understand but in the case that you need it to be broken down I have given a breif breakdown below.
We declare our structure which has a bit of a different anatomy than a standard data type, instead this data type holds what seems to be a json value. This is known as a tagged data type which vasically defines metadata or instructions to the encoding/json package or other libraries that may use these special tags. Typically, you only see this in programs that may be using JSON to send or parse server responses, load configuration files, load settings files or is storing data in specific formats for the use of code translation. In our main function, we can see that we take our structure and specify data like normal and then finally convert it to json by calling json.Marshal
which will convert the data as required.
Our output may be something like the following
Struct
| Method Sets
Method sets with structures is basically a way to define a function or method within a structure where that structure can only access that function. Below is an example of a program that creates a structure that utilizes method sets.
As you can see, like any normal structure we declare the variables we need inside of the circle structure and then we create a method structure to calc the area of a circle. This function can not be called without properly initializing the Circle
structure. When we declare a valid structure and use 10.4 as the radius we can then call the function to get the area of the circle.
Last updated