Posts

Showing posts from March, 2020

Golang Conversions – Ints To Strings And Strong Typing

Image
Go is a strongly typed language, which means at any point a developer should know exactly what type of value they are dealing with. For example, if we have a function that prints a string, we can’t just give it an integer and expect it to work. We have to cast it to a string explicitly: func main() { num := 5 numString string(num) printString(numString) } func printString(s string) { fmt.Println(s) } If we don’t cast the value, the go compiler won’t even let us compile the program. Dynamic Typing is Slow Developers coming from dynamically typed languages often get annoyed with Go’s strong typing. They think that the compiler should just know what they mean and do the type cast implicitly. Strongly typed languages won't guess for you. They make you make the decisions. Click To Tweet There is a reason for this. Type conversions take time and resources. If the Go runtime were to dynamically type every value then programs would run a lot

How To Separate Library Packages in Go

Image
I’ve often seen, and have been responsible for, throwing code into packages without much thought. I’ve quickly drawn a line in the sand and started putting code into different folders (which in Go are different packages by definition) just for the sake of findability. Learning to properly build small and reusable packages can take your gopher career to the next level. Click To Tweet What is a Package? In Go, code is organized into packages. Every folder that contains Go code is a package. Runnable programs must have a package called “ main ” which acts as an entry point to the program. All other packages can be named (almost) anything, and they export code that can be used in other packages and runnable programs. These kinds of non-runnable packages we call “ library ” packages by convention. Library packages allow developers to export code so it can be used by the outside world. Packages are essentially APIs where exported functions are user-facing and unexported functions

I Wrote Go-TinyDate, The Missing Golang Date Package

Image
time.Time makes dealing with dates and times in Go a breeze, and it even comes bundled in the standard library! However, a time.Time{} struct uses more than 24 bytes of memory under most conditions, and I’ve run into situations where I need to store millions of them in memory, but all I really needed was a UTC date! Go-TinyDate solves this with just 4 bytes of memory. Star the Github! https://github.com/lane-c-wagner/go-tinydate How? Let’s look at the time.Time struct: type Time struct { wall uint64 // 8 bytes ext int64 // b bytes loc *Location // 8 bytes if not nil, plus location memory } type Location struct { name string // unlimited zone []zone // unlimited tx []zoneTrans // unlimited cacheStart int64 // 8 bytes cacheEnd int64 // 8 bytes cacheZone *zone // 8 bytes if not nil, plus zone } type zone struct { name string // unlimited offset int // 4-8 bytes depending on OS

Golang Mutexes – What Is RWMutex For?

Image
Golang is King when it comes to concurrency. No other language has so many tools right of-of the box, and one of those tools is the standard library’s sync.Mutex{} . What Problem Do Mutexes Solve? We don’t want multiple threads accessing the same memory at the same time. In concurrent programming, we have many different threads (or in Go, goroutines ) that all potentially have access to the same variables in memory. One case that mutexes help us avoid is the concurrent read/write problem . This occurs when one thread is writing to a variable while another variable is concurrently reading from that same variable. The program will panic because the reader could be reading bad data that is being mutated in place. What Is A Mutex? Mutex is short for mutual exclusion. Mutexes keep track of which thread has access to a variable at any given time. Let’s see some examples! Consider the following program: package main import ( "fmt" ) func main() { m

Best Practices For Writing Clean Interfaces in Go

Image
Interfaces allow us to treat different types as the same type when it comes to specific behaviors. They are central to a Go programmers toolbelt and are often used improperly by new Go developers… leading to hard to read and buggy code. Interfaces are named collections of method signatures, they are how we achieve a kind of polymorphism in Go. Click To Tweet Recap on Interfaces Let’s look to the standard library as an example of the way to write clean Go. The error interface is simple: type error interface { Error() string } By definition, the error interface encapsulates any type that has an Error() method defined on it, accepts no parameters, and returns a string . For example, let’s define a struct that represents a network problem: type networkProblem struct { message string code int } Then we define an Error() method: func (np networkProblem) Error() string { return fmt.Sprintf("network error! message: %s, code: %v", np.m

Wrapping Errors in Go – How to Handle Nested Errors

Image
Errors in Go (Golang) are a hot topic. Many newcomers to the language immediately level their first criticism, "errors in go are clunky! Let me try and catch!" This criticism is well-meaning but misguided. Click To Tweet By Lane Wagner @wagslane on Twitter The paradigm of errors as a type, rather than something to be thrown and cause panics, allows for more control of how to handle “bad” state. It also forces developers to think about errors at every step. What will go wrong here? How should I handle it? There are plenty of articles that discuss the pros/cons of error handling in Go. I want to talk specifically about how the clunky (albeit better) handling of errors in Go can lead to a common problem: nested errors. The Called Function To demonstrate the problem of nested errors, let’s take a look at the following function: func isInRange(i int) error { const min = 5 const max = 10 if i < 5 || i > 10 { return fmt.

Base64 vs Base58 Encoding

Image
By Lane Wagner –  @wagslane  on Twitter Base64 is one of the most popular encoding formats for representing data. Have some binary data? Base64 encode it for convenient readability and parsing. Base58 is just another encoding format (with 58 characters instead of 64, and has gained popularity largely due to Bitcoin and other cryptocurrencies. When it comes to data encoding, there is typically a trade-off made between: Human Readability: Do humans have a good idea of what is being represented at a glance? Efficient data compression: How many bytes are used to represent the same data? Let’s rank Base58, Base64, and ASCII encoding against each other using these metrics! First: A Note on Binary All data is stored in a raw binary format on computers. These encoding formats (Base64, Base58, and ASCII) are just different ways of reading and writing binary data. For example, in Base64 the binary code 000000 represents the letter A , but in ASCII the binary 00000000 represents the