Chris Padilla/Blog
My passion project! Posts spanning music, art, software, books, and more
- Less overhead structurally
- Less complex to write and manage
- Threads in other language are more expensive as far as memory used compared to Go
Goroutines, Structs, and Pointers in Go
A potpourri of Go features I dove into this week!
Structs
Last week I looked at the Maps data type. It works similar to a JS Object, except all values must be the same type.
Structs in Go are a data structure for saving mixed Data Type values. Here's what they look like:
// Type Declaration
type UserData struct {
firstName string
lastName string
email string
numberOfTickets uint
}
// Can then be used in array or slice
var sales = make([]UserData, 0)
var userData = UserData {
firstName: "Chris",
lastName: "Padilla,
email: "hey@chris.com",
numberOfTickets: 2,
}
// accessing values:
booking.firstName
// in a map, you would use bracket syntax
// booking["firstName"]
This is comparable to a lightweight class in languages like Java and C#. And, of course, the type declaration will look familiar to any TypeScript users.
Goroutines
This is what I've been waiting for!
Goroutines are the key ingredient for Go's main benefit: Lightweight and easy to use concurrency.
The strengths of Go over other languages:
Goroutines are an abstraction of an actual OS thread. They're cheaper and lightweight, meaning you can run hundreds of thousands or even millions without affecting the performance of your application.
Java uses OS threads, takes a longer startup time. Additionally, those threads don't have an easy means of communicating with each other. That's made possible through channels in Go, a topic for another day!
Sample Code
The syntax for firing off a concurrent function is pretty simple:
// Do something expensive and Block the thred
sendTickets()
// Now try this non-blocking approach, creating a second goroutine
go sendTickets()
There's a bit of extra work needed to align the separate Goroutine with your main function. If the above code was all that was in our program, the execution would exit before the result from sendTickets()
was reached.
Go includes sync
in it's standard library to help with this:
import (
"sync"
"time"
)
var wg = sync.WaitGroup{}
wg.Add(1)
go sendTickets()
wg.Wait()
func sendTickets() {
// Do something that takes a while.
wg.Done()
}
When we create a Wait Group, we're essentially creating a counter that will keep track of how many concurrent methods have fired. We add to that counter before starting the routine with wg.Add(1)
and then note when we want to hold for those goroutines to end with wg.Wait()
Already, with this, interesting possibilities are now available! Just like in JavaScript, you can set up a Promise.all()
situation on the server to send off multiple requests and wait for them all to return.
Pointers
Pointers are the address in memory of a variable. Pointers are used to reduce memory usage and increase performance.
The main use case is in passing large variables to functions. Just like in JavaScript, when you pass an argument to a function, a copy is made that is then used within that function. If you were to pass the pointer instead here, that would be far less strain on the app's memory
func bookTickets(largeTicketObject TicketObj) string {
// Do stuff with this large object
return "All set!"
}
Also, if we made changes to this object and wanted the side effect of adjusting that object outside the function, they wouldn't take effect. We could make those changes with pointers, though.
To store the pointer in a var, prepend & to the variable name:
x := 5
xPointer = &x
fmt.Println(xPointer)
// Set val through pointer
*xPointer = 64
fmt.Println(i) // 64
Faber - Fantasia Con Spirito
Spooky music month! This one feels very "Cave of Wonders" from Aladdin. š§āāļø
Jet Set Radio Vibes
I give one listen to the Jet Set Radio Soundtrack, and now I get some funny 90s hip hop inspired ditties in my recomendations. Not bad!
Been doing lots of gesture drawing digitally to help get used to the pen. It's a surprising jump from traditional to wacom!
Lastly, I'm sorry to say that Anthony Clark bore the horrible news: #Warioctober has arrived.
I regret to inform you that today is the beginning of #Warioctober , where everyone, be they just or wicked, must honor the terrible creature Wario. Here is Classic Wario. May we be forgiven for what we must do. pic.twitter.com/i99DQTjToR
— Anthony Clark (@nedroid) October 1, 2023
I've tried to escape it, but the punishment for skipping out is much more fowl than the ritual itself.
Tourist's Guide to Go
I'm dipping my toes into Go! I became interested after hearing about how accessible it makes multi-threaded logic to coordinate, especially on cloud platforms.
I've been there with JavaScript and Python: it could be handly for processes running in tandem having the ability to communicate. Seems like Go opens this communication channel in a way that's easier to manage than a language such as Java. (All from hearsay, since I've not dabbled in Java just yet.)
I'm excited to dig into all that! But, as one does, first I'm relearning how string templating works in the new language:
Compile Time Errors
When getting ready to run a main.go
file, checks will be made to ensure there aren't any procedural errors. Go checks for unused variables, ensures types match, and that there are no syntax errors. A nice feature for a server side language, keeping the feedback loop tight!
Static Typing
Like C# and Java, Go is statically typed. On declaration, variables need to be assigned a type.
var numberOfCats uint
When assigning, the type can be inferred:
const conferenceTickets = 50
Variables
Constants and mutable variables exist here. Somewhat like JavaScript, the const
keyword works, and var
functions similarly to how it does in JavaScript. No let
option here.
A shorthand for declaring var is with :=
:
validEmail := strings.Contains(email, "@")
This does not work with the const
keyword, though.
Slices and Arrays
Arrays, similar to how they work in lower level languages, must have a set length defined on declaration:
var bookings [50]string
Slices, however, are a datatype built on top of Arrays that allow for flexibility.
var names []string
Both require that all elements be of the same type, hence the string
type following []
.
Maps
Maps in Go are synonymous with Dictionaries in Python and Objects in JavaScript. Assigning properties can be done with the angle bracket syntax:
newUser := make(map[string]string)
newUser["firstName"] = userName
newUser["email"] = email
newUser["tickets"] = strconv.FormatUint(uint64(userTickets), 10)
Maps can be declared with the make
function:
newUser := make(map[string]string)
For Loops
There's only one kind of For loop in Go. Syntactically, it looks similar to Python where you provide a range
from a list to loop through:
for _, value := range bookings {
names := strings.Fields(value)
firstName := names[0]
firstNames = append(firstNames, firstName)
}
Range here is returning an index and a value. Go will normally error out if there are unused variables. So to denote an intentional skip of the index property, the underscore _
will signal to go that we're ignoring this one.
Package
Like C# Namespaces, files in go can be grouped together as a package. You can do so with the package keyword at the top of the file:
package main
import (
"pet-app/cats"
"fmt"
"strconv"
"strings"
)
func main() {...}
Notice "pet-app/cats": here we can import our packages within the same directory with this syntax. "pet-app" is the app name in the generated "go.mod" file, and "cats" is the package name.
Public and Private Functions
By default, functions are scoped to the file. To make it public, use a capital letter in your function name so it may be used when imported:
func BookTickets(name string) string {
// Logic here
return res
}