Today I Learned

hashrocket A Hashrocket project

24 posts about #go surprise

Replace first letter of string

You need to use runes to prevent corrupting unicode values:

package main

import (
    "fmt"
)

func replaceFirstRune(str, replacement string) string {
    return string([]rune(str)[:0]) + replacement + string([]rune(str)[1:])
}

func main() {
    name := "Hats are great!"
    name = replaceFirstRune(name, "C")
    fmt.Println(name)
}

Output:

=> Cats are great!

Just like in ruby, this doesn't cover multi-byte unicode characters. You still need to do a unicode table lookup:

name = "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"
name[0] = "C"
=> "Cโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"
println(replaceFirstRune("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", "C"))
=> "Cโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"

You can go step more and replace the man with a woman:

println(replaceFirstRune("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", "๐Ÿ‘ฉ"))
=> "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"

Have OS specific implementations in golang

In go, you can create separate implementations for different operating systems by creating multiple files:

package main

import (
    "play.ground/foo"
)

func main() {
    foo.HelloComputer()
}

-- go.mod --
module play.ground

-- foo/foo_windows.go --
// +build windows

package foo

import "fmt"

func HelloComputer() {
    fmt.Println("Hello windows!")
}

-- foo/foo_linux.go --
// +build !windows

package foo

import "fmt"

func HelloComputer() {
    fmt.Println("Hello not windows!")
}

Go Tests Uncached

Go caches test results, and this can be a problem if you have flickers, are benchmarking your tests, or just want to run all the tests every time.

Disable caching with GOCACHE:

$ go test ./...
ok      monkey/ast      (cached)
ok      monkey/lexer    (cached)
ok      monkey/parser   (cached)
$ 
$ GOCACHE=off go test ./...
ok      monkey/ast      0.005s
ok      monkey/lexer    0.005s
ok      monkey/parser   0.005s

Captured for var in closure can change

Variables can be captured into anonymous functions in go with closures, and its easy to use the variables we declare in a for loop in those anonymous functions, but that doesn't produce the results I would expect.

var fnArray []func()

for _, chr := range []string{"a", "b", "c"} {
    fmt.Println("Hello,", chr)
    fn := func() {
      fmt.Println("Goodbye,", chr)
    }
    fnArray = append(fnArray, fn)
}

for _, fn := range fnArray {
  fn()
}

The above code outputs:

Hello,a
Hello,b
Hello,c
Goodbye,c
Goodbye,c
Goodbye,c

Go optimizes for loops by using the same memory space for each value that it terates over. To avoid this hard edge you can reassign that value:

capturedChr := chr

Inserted that assignment into our for loop looks like this:

var fnArray []func()

for _, chr := range []string{"a", "b", "c"} {
    capturedChr := chr
    fmt.Println("Hello,", chr)
    fn := func() {
      fmt.Println("Goodbye,", capturedChr)
    }
    fnArray = append(fnArray, fn)
}

for _, fn := range fnArray {
  fn()
}

And produces this more correct output:

Hello,a
Hello,b
Hello,c
Goodbye,a
Goodbye,b
Goodbye,c

Checkout the go playground here

Using an array literal to specify length

Go forces you to specify the length of an array when creating or declaring an array. If you don't declare the length then its not an array its a slice. The syntax for declaring the array looks like this:

// array of length 3
var numbers = [3]int{1, 2, 3}

When we use an array literal with the {} to declare the values of an array, the length declaration becomes redundant.

//slice
var numbers = []int{1, 2, 3}

The above example, however shows the declaration of a slice not an array. To let the literal define the length of an array we can place the ... operator inside the [] square brackets.

var numbers = [...]int{1, 2, 3}

Now, we can add a fourth element to the array without also having to change the specified length.

Use Go Printf argument twice with adverbs

Usually, when you use one of the format functions from the fmt library you pass in one argument or operand for each verb you use.

fmt.Printf("Hello %s, nice to meet you %s", "Lauren", "Harry")
// Prints --> Hello Lauren, nice to meet you Harry

But what if you want to use one of those operands twice? Well, you can use what go terms an adverb. Place square brackets after the percent symbol with an index that refers to the argument you'd like to use.

fmt.Printf("Hello %s, nice to meet you %[1]s", "Lauren")
// Prints --> Hello Lauren, nice to meet you Lauren

In Go, NaN does not equal NaN

It's false, it returns false.

fmt.Println(math.NaN == math.NaN)

Don't fret though, there is a function that can tell you whether or not a value is NaN.

fmt.Println(math.IsNaN(math.Log(-1.0)))

The above returns true and all is well with the world. Don't use math.NaN as a sentinel value, it does not work, use the math.IsNaN function.

Build For A Specific OS And Architecture

Go programs can run anywhere, but you've got to create builds specific to each operating system and architecture. This can be done when building by specifying the GOOS and GOARCH environment variables.

For example, if you'd like to build a 32-bit Linux distribution:

GOOS=linux GOARCH=386 go build -o linux_386_build

The GOOS value specifies the operating system as Linux and the GOARCH value of 386 specifies a 32-bit architecture.

The plethora of GOOS and GOARCH options can be found here.

Three syntactical elements that return 2 values

golang supports multiple return values from functions but there are also 3 different syntactical elements or accessors that also return 2 values.

The most straight forward is accessing values from a map.

colors := map[string]string{
  "ocean":  "blue",
  "forest": "green",
  "jet":   "black"
}

x, ok := colors["ocean"]

Type assertions also want to let you know that they're ok, like when we try to assert an error as a json.SyntaxError. Yeah, its a SyntaxError, ok?

synerr, ok := error.(*json.SyntaxError)

Channels can also let the program that things are going ok when using the receive syntax <-.

receivedValue, ok := <- ch

Go docs at the command line

Go has plentiful documentation online, but sometimes using a search engine to find the right thing in go can be tough. Go has docs at the command line though.

You can go doc <name-of-package>

> go doc io/ioutil
package ioutil // import "io/ioutil"

Package ioutil implements some I/O utility functions.

var Discard io.Writer = devNull(0)
func NopCloser(r io.Reader) io.ReadCloser
func ReadAll(r io.Reader) ([]byte, error)
func ReadDir(dirname string) ([]os.FileInfo, error)
func ReadFile(filename string) ([]byte, error)
func TempDir(dir, prefix string) (name string, err error)
func TempFile(dir, prefix string) (f *os.File, err error)
func WriteFile(filename string, data []byte, perm os.FileMode) error

Or, you can go doc <function> if the function is fully qualified with the package name.

> go doc io/ioutil.WriteFile
func WriteFile(filename string, data []byte, perm os.FileMode) error
    WriteFile writes data to a file named by filename. If the file does not
    exist, WriteFile creates it with permissions perm; otherwise WriteFile
    truncates it before writing.

go doc ioutil.WriteFile will also work.

Access Go Docs Offline

The Go language has a wonderfully comprehensive standard library. There is documentation for all of it. You can access that documentation anytime if you have an internet connection via https://golang.org/doc/.

If you are without an internet connection, you're still in luck. Go has a built-in feature for serving the documentation locally offline. Just run the following command:

$ godoc -http=:6060

and then visit localhost:6060.

source

Upgrading From An Older Version On Mac

To upgrade from an older version on Mac, there are a couple manual steps that you need to take. For starters, download the latest installer for Mac from Go Lang Downloads.

While this is downloading, you'll need to delete the older version of Go that is installed on your machine.

First, remove the existing Go installation directory:

$ sudo rm -rf /usr/local/go

Second, clean up the Go bin directory from your PATH environment variable:

$ sudo rm /etc/paths.d/go

Now, you can double click on the downloaded installer dmg and follow the prompt instructions.

When its all said and done, check go version from the command line to see that you are now working with the latest.

Private vs Public Struct members

In Golang, identifiers can be either Exported or Unexported. In other languages this would be known as public or private, but in golang the question is whether the identifiers should be able to be seen outside of the module. If not? Don't export them.

The rule is simple, if an identifier is capitalized it will be exported. This manifests itself in golang when converting a struct to JSON.

type Apple struct {
    color string
    weight int
}

a := json.Marshal(Apple{"green", 10})
fmt.Println(a)

In this case this code will print {}. To print what you would expect the struct should be defined with capitalized identifiers:

type Apple struct {
    Color string
    Weight int
}

Now the marshalled struct should be {"Color": "green", "Weight": 10}

Installing the Golang tools with Vim

Go has a set of tools to help aid in go development such as a test coverage tool for go or go guru (a tool for answering questions about the go source code.

While in vim if you would like to install these tools just use the command:

:GoInstallBinaries

And likewise if you would like to update these tools use the command

:GoUpdateBinaries

These commands are provided by the vim-go vim plugin. The binaries are installed into your $GOPATH directory or if you'd like to override that dir set the g:go_bin_path vim variable.

Quick Garbage Collector Stats from Go Programs

Set the GODEBUG=gctrace=1 in the environment to get log output from a Go program whenever the garbage collector runs.

Example:

gc 1 @10.282s 0%: 0.12+0.17+0.10 ms clock, 0.38+0/0.053/0.19+0.30 ms cpu, 4->4->0 MB, 5 MB goal, 4 P

The clock and cpu sections give us details on how long the GC cycle took. In this case we can see that it took well under 1ms.

See https://golang.org/pkg/runtime/ for details on what the output means.

Sleep For A Duration

Many languages allow you to sleep for a certain number of milliseconds. In those languages, you can give 500 or 1000 to the sleep function to sleep for half a second and a second respectively. In Go, the duration of a call to time.Sleep is in nanoseconds. Fortunately, there are constants that make it easy to sleep in terms of milliseconds.

For example, you can sleep for a half a second (500 milliseconds) like so:

package main

import (
    "time"
)

func main() {
    time.Sleep(500 * time.Millisecond)
}

Other available time constants are Nanosecond, Microsecond, Second, Minute, Hour.

Seeding Golang's rand

Random numbers in Go don't seem random? Surprise! The rand package defaults to a seed of 1.

That's great if you need a bunch of random numbers at the start of your program. Not great if you expect a different outcome each time you run the program.

A solution is to seed rand with Unix time. Try it in the init() function:

package main

import (
    "math/rand"
    "time"
)

func init() {
    rand.Seed(time.Now().UTC().UnixNano())
}

Replace The Current Process With An External Cmd

Go's syscall.Exec function can be used to execute an external program. Instead of forking a child process though, it runs the external command in place of the current process. You need to give the function three pieces of information: the location of the binary, the pieces of the command to be executed, and relevant environment variables. Here is a simple example.

package main

import "fmt"
import "os"
import "syscall"

func main() {
    // get the system's environment variables
    environment := os.Environ()

    // get a slice of the pieces of the command
    command := []string{"tmux", "new-session", "-s", "burrito"}

    err := syscall.Exec("/usr/local/bin/tmux", command, environment)
    if err != nil {
        fmt.Printf("%v", err)
    }
}

When this program is executed, it will replace itself with a new tmux session named burrito.

Go iota

Go has an interesting feature called iota. When declaring a list of constants, this keyword represents successive untyped integer constants.

const (
    foo = iota  // foo == 0
    bar = iota  // bar == 1
    baz = iota  // baz == 2
)

Anytime const is invoked, the counter resets.

const foo = iota  // foo == 0
const bar = iota  // bar == 0

This is a cool way to quickly define a list of integer constants, such as 'true' and 'false', for later use.