Modules

When it comes to writing bigger projects in language's like Golang, you might find yourself needing to use multiple files, this module will help you understand the structure of modules in golang and how to properly use them. This module will also contain information over the rule sets.

Modules | Rules

When it comes to importing specific files or rather modules since Go does not have a direct "concept of files", you need to follow rules. These rules help the compiler understand what you are trying to create and what you are trying to achomplish by importing what. Now, below I have named a chart of the primary rules for importing modules in Go.

Rule
Compiler Error
Example Error

Import path must be enclosed in double quotes

import "mypackage"

import mypackage

Imported package must be available in the Go module or standard library

import "fmt"

import "nonexistent"

Unused imported packages result in a compilation error

N/A

import "unused"

Importing a package but not using any of its exported symbols will cause a compilation error

N/A

import _ "unused"

Cyclic imports are not allowed

N/A

N/A

These rules might be quite confusing so lets break it down a bit

Import path must be enclosed in double quotes

This error means that the import path was not enclosed using " or ". Importing must be enclosed in either "" or `` marks

Importing package must be avalible in the go module or standard library

This can mean that the module you tried to import does not exist within the standard library or does not exist within your .mod file configuration

Unused import packages

This means that you imported a package but did not use it

package main 

import "fmt"

func main() {
	println("hello")
}

this program called println not fmt.Println() so fmt is unused

Importing a package but not using any of its exported symbols

same thing as unused import but instead importing as for example

package main 

import f "fmt"

func main() {
     println("hi")
}

Cyclic imports are not allowed

This basically means that a package that depends on another package can not be imported.

For example a cyclic import occurs when two or more packages depend on each other directly or indirectly this means that it creates a circular dependency where Package A imports Package B, and Package B also imports Package A, either directly or through a chain of dependencies.

Importing rules are pretty easy to understand but can become a pain if you do not exactly know what you are and arent using.

Modules | Creating Packages

When it comes to creating modules in Golang, it can be quite confusing for people so lets walk through a small module that allows you to auto calculate specific numbers.

We have a file tree that looks like this

Project
├── go.mod
├── main.go
└── Modules
    ├── Add.go
    └── Sub.go

1 directory, 4 files

We first notice that there is a go.mod file within the directory tree, how did we get that file? In order to make project files or modules we must first create a go.mod file. The program we are going to be building is a simple calculator that can add and subtract two numbers x and y. With that information, we want our main module to be Calc thus the command go mod init Calc which will create a go.mod file that looks like the one below.

module Calc

go 1.17

The module is the module name in this case Calc and the go 1.17 is the version of the programming language. Simple file, now what does this mean? This means that anytime we import a filepath or module we need to use this filepath to import a module. So, within our go.mod file we need to import "Calc/Modules" as we can not directly import the "Modules" file path if it is not assigned the module within the go.mod file. Lets take a peek at our main.go file and see whats inside.

package main

import (
	Caclulation_Module "Calc/Modules"
	"fmt"
)

func main() {
	r1 := Caclulation_Module.Add(20, 30)
	r2 := Caclulation_Module.Subtract(20, 30)
	fmt.Println("Added      | 20 + 30 = ", r1)
	fmt.Println("Subtracted | 20 / 30 = ", r2)
}

When we look at this file we see that we are importing the Calc/Modules package with the name Calculation_Module which is how we will call functions or variables from those packages.

We have two functions corresponding to our file names which are called Add and Subtract which will add and subtract two values. When we look in both files we see the following.

Add.go

package Calculator

func Add(x, y int) int {
	return x + y
}

Sub.go

package Calculator

func Subtract(x, y int) int {
	return x - y
}

Notice how each file has the same package name Calculator? This is because Go wants you to add the name of the package that is defined within that same exact directory, if we were to create a folder for each individual package or source code file then we could add individual names but then we would need to change our main.go file to import both of those individual sub paths.

Another note is that you see how each function starts with a capital letter? Golang will only allow you to use functions, variables, structures, imports etc ONLY if they start with a capital letter. This is because Golang only wants to export specific groups rather than everything. This allows the developer to have more control over what they want to be exported into the primary environment. You will also see this in every standard library that Golang has, every function or variable called will have a capital letter at the start of it.

Modules | Using third party packages

Something quite amazing about go is that it does not require a direct package installer like PyPi or Gem or even specific domain names or services to be used. Instead, golang will be able to download a specific package as long as it has a go.mod file within its root, this means that files can be downloaded from quite a large variety of hosting services. The program shown below uses the gopacket third party library to capture packets and print them out!

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/google/gopacket"
	"github.com/google/gopacket/pcap"
)

func main() {
	// Define the network interface to capture packets from
	device := "eth0"

	// Open the device for packet capture
	handle, err := pcap.OpenLive(device, 65536, true, pcap.BlockForever)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	// Set packet filter (optional)
	filter := "tcp and port 80"
	if err := handle.SetBPFFilter(filter); err != nil {
		log.Fatal(err)
	}

	// Start capturing packets
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())

	// Process captured packets
	for packet := range packetSource.Packets() {
		fmt.Println(packet)
	}
}

But how do we install this library? We can use a command known as go get to install this library!

go get github.com/google/gopacket

this command will install it and add it to your go.sum file which looks like the one shown below!

github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

What are all of these doing here? Well, third party packages rely on other third party packages which is why we see so many different packages here! But you may be confused why the go.sum file contains cryptographic hashes within each package. Well, golang does this to make sure that the original information has not been tampered with since it has been downloaded and added to the repo. These hashes can basically allow golang to verify that information. Go does this so it can provide stronger guarantees of package integrity and security in turn it helps prevent potential attacks where an adversary might try to inject malicious code into a package or intercept and modify the package during download.

Now we can easily use these third party packages!

Last updated