top of page
Writer's pictureTravis Martin

Golang: pointers, channels and OOP (ish)

Making a quick post on few of golang's concepts that can be confusing.


Pointers and references


I am writing a quick post on golang (go) pointers (*) and references (&), when to use them which data structures are value types and which are reference types.


Pointers

This tells go: Give me the value this memory address is pointing at. Another way to say it, we pass go the memory location and it gives us the data thats stored there. Looking at this like a programming data structure HashMap, Key/Value Pair, this would be like passing the key and getting the VALUE.


References

This tells go: Give me the memory address of the value this variable is point at. Another way to say it, give me the memory location where the this data exists in memory. This can be looked at like a hashmap Key/Value pair, references return the KEY, and pointers return the VALUE.


Value Types

Value types in go use pointers to change the values in a function. Since go is a pass by value language it will make a copy of the value in memory and pass the copy to the function, any modifications made to the variable (in the function) will not be reflected to the global variable after the function completes. There will be an example below of the type of datatypes are Value types and a code example.

int
float
string
bool
structs

Code Example:

type company struct {
    name string
}
// This function will not work as expected see output below.
func (c company) updateName(name string) {
    c.name = name
}

func main() {

    name := company{
        name: "rickjms",
    }

    fmt.Printf("before update: %+v\n", name)
    name.updateName("testing")
    fmt.Printf("After update: %+v\n", name)
}

Output (name does NOT get updated)

before update: {name:rickjms}
After update: {name:rickjms}

Fix: add the pointer to the Receiver function (c *company) this will tell go that we are not updating the COPY of the data in the function. We are updating the data stored in the pointer passed in. Remember go is a pass by value language, so any data updated will NOT update the global variable but instead the local variable. To avoid this we pass the pointer to the global variable, this will enable us to update that value.


Fix:

func (c *company) updateName(name string) {
    c.name = name
}

Reference Types

Reference types by default are pointers themselves and do NOT require pointers to change the data. Similar to Value types go passes a COPY of the reference (pointer) to the function so any modifications made on the data within the function will get updated on the data structure once the function completes.

slices
maps
channels
pointers (pointer types)
functions

Code example:

package main

import (
 "fmt"
)

func main() {
 colors := map[string]string{
      "red":   "#ff0000",
      "green": "#4bf745",
    }

 colors["white"] = "#ffffff"

 fmt.Println("Before update")
 printMap(colors)
 updateMap("Blue", "#rgb123", colors)
 fmt.Println("After update")
 printMap(colors)
}

func printMap(c map[string]string) {
 for color, hex := range c {
        fmt.Println("Hex code for", color, "is", hex)
    }
}

func updateMap(key, value string, c map[string]string) {
    c[key] = value
}

Output (hex codes are made up)

The hex codes are obviously incorrect just using them as an example. The key point here is we use the function updateMap(key, value, map) and it directly changes the colors map.

~/maps: go run main.go
Before update
Hex code for red is #ff0000
Hex code for green is #4bf745
Hex code for white is #ffffff
After update
Hex code for Blue is #rgb123
Hex code for red is #ff0000
Hex code for green is #4bf745
Hex code for white is #ffffff

Using Channels for Concurrency and maybe Parallelism

The argument here is, if we have 1 core system this would not "Technically" be parallelism, and would lean more on the concurrency. Go created the syntax to easily allow adding an additional go routine calling: go <Function call>

Go scheduler will handle all the go subroutines in the background for you, and you do not need to try and manipulate it. When go encounters a "Blocking call" such as running http.get(web_url) call, the go scheduler will continue with the execution of the program until the blocking portion of the code completes. Once the blocking code completes you can send a message back to the main go routine VIA channels.


Code

I will be making use of the http package, this will be a small and simple script like program that will check the status of the urls provided. I use a similar one for bugbounties called httprobe. This will not in anyway replace said script, more of a simplified version.


I implemented this using two different patterns. First approach using channels and the second approach using waitgroups, channels and adding in a way to control the number of threads that can be in use.

package main

import (
    "fmt"
    "net/http"
    "sync"
)

func main() {
    links := []string{
        "http://google.com",
        "http://youtube.com",
        "http://facebook.com",
        "http://stackoverflow.com",
        "http://golang.org",
    }

    c := make(chan bool)
    for _, link := range links {
        go checkLink(link, c)
    }

    // Blocking function here wait for channel response
    for _, link := range links {
        if <-c {
            fmt.Printf("%v is up\n", link)
        } else {
            fmt.Printf("%v is down\n", link)
        }

    }
    close(c)

    // Using Wait groups, thread control and channels
    var waitGroup sync.WaitGroup
    urls := make(chan string)
    threadCount := 6
    for i := 0; i < threadCount; i++ {
        waitGroup.Add(1)
        go func() {
            for u := range urls {
                resp, err := http.Get(u)
                if err != nil {
                    fmt.Printf("Error on %s: %s\n", err, u)
                    continue
                }
                fmt.Println("Working:", u)
                fmt.Printf("%s status code:%s\n", u, resp.Status)
            }
            waitGroup.Done()
        }()
    }

    fmt.Println("Using channel approach wait groups")
    for _, url := range links {
        urls <- url
    }
    close(urls)
    waitGroup.Wait()
}

func checkLink(link string, c chan bool) {
    _, err := http.Get(link)
    if err != nil {
        c <- false
        return
    }
    c <- true
}

Output (Both approaches outputted below)

http://google.com is up
http://youtube.com is up
http://facebook.com is up
http://stackoverflow.com is up
http://golang.org is up

Using channel approach wait groups
Working: http://stackoverflow.com
http://stackoverflow.com status code:200 OK
Working: http://facebook.com
http://facebook.com status code:200 OK
Working: http://golang.org
http://golang.org status code:200 OK
Working: http://youtube.com
http://youtube.com status code:200 OK
Working: http://google.com
http://google.com status code:200 OK

Golang object oriented programming (ish)

Go interfaces are implemented implicitly, meaning they do NOT have a "implements" keyword to be associated to an interface. You just create the struct and the receiver functions that are defined in the interface and they become part of the interface group.


Code Example: (Output in code snippet)

package main

import (
    "fmt"
)

type logger interface {
    log(message string)
}

type logToFile struct {
}

type logToStdout struct {
}

func main() {
    fileLogger := logToFile{}
    stdOutput := logToStdout{}

    // Lets make a list of loggers and the created loggers to it
    var loggers []logger
    // Adding the above 2 loggers
    loggers = append(loggers, fileLogger, stdOutput)

    for _, logger := range loggers {
        logger.log("Hello world")
    }
}

func (lstd logToStdout) log(message string) {
    fmt.Println("[STDOut]:", message)
}

func (ltf logToFile) log(message string) {
    // Do some file logging here
    fmt.Println("[LOGGING TO FILE]:", message)
}

/* ------- OUTPUT -------
[LOGGING TO FILE]: Hello world
[STDOut]: Hello world
*/

The code is pretty straightforward and nothing overly complicated other then the receiver functions where this is a "go thing" this is how go associates functions to its struts. There are a lot of resources online for this I mainly just want to show a code example on how to do polymorphism in go.


Summary

Go is a pretty fun language to learn and use, I have never used points in other languages like C, and C++ before so that concept was foreign to me. I enjoy building command line interface tools with go, the process is simplified and the language allows you to generate a binary based on your OS.

1,569 views0 comments

Comments


bottom of page