Go Tips

Here lies my collection of random things I've learned how to do in Go.

Marking a Context as Done with a Signal

2025-07-12

The signal package has a useful function called NotifyContext that will notify the Done channel of the Context when one of the listed signals is received by the running process.

Watch out! When you use this, the exit method will no longer be called when an interrupt is received, so you'll need to call it yourself.

package main

import (
        "context"
        "fmt"
        "os"
        "os/signal"
)

func main() {
        ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)

        fmt.Println("press Ctrl-C to exit...")

        // waits
        <-ctx.Done()
        os.Exit(0)
}

Emulating Closed Sum-Type / Discriminated Unions using the AsAny Pattern

2025-05-04

OpenAI's Go library makes extensive use of this, and I quite like it.

The trick is to use a sealed-interface union wrapper. It's a struct with mutually-exclusive pointer fields plus an unexported marker interface. You don't use the wrapper directly; use constructors to instantiate new instances.

package main

import "fmt"

type Dog struct{}

func NewDog(d Dog) PetUnion {
	return PetUnion{OfDog: &d}
}

func (Dog) bark() { fmt.Println("bark") }

type Cat struct{}

func NewCat(c Cat) PetUnion {
	return PetUnion{OfCat: &c}
}

func (Cat) meow() { fmt.Println("meow") }

// keep this private
type anyPet interface {
	implAnyPetUnion()
}

func (Dog) implAnyPetUnion() {}
func (Cat) implAnyPetUnion() {}

type PetUnion struct {
	OfDog *Dog
	OfCat *Cat
}

func (u PetUnion) AsAny() anyPet {
	switch {
	case u.OfDog != nil:
		return u.OfDog
	case u.OfCat != nil:
		return u.OfCat
	}
	return nil
}

func Example() {
	pet := NewDog(Dog{})
	switch v := pet.AsAny().(type) {
	case *Dog:
		v.bark()
	case *Cat:
		v.meow()
	}
	// Output:
	// bark
}

Recurring Tasks

2025-04-28

package main

import "time"

func NewHourlyCron(f func()) {
    ticker := time.NewTicker(time.Hour)
    defer ticker.Stop()

    for {
        select {
        case <-ticker.C:
            f()
        }
    }
}

Format Time in Kitchen Time

2025-04-28

package main

import (
	"fmt"
	"time"
)

func Example() {
	fmt.Println(time.Now().Format(time.Kitchen))
	// Output:
	// 11:00PM
}

Capitalize Words

2025-04-28

package main

import (
	"fmt"

	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

func Capitalize(s string) string {
	return cases.Title(language.English).String(s)
}

func ExampleCapitalize() {
	fmt.Println(Capitalize("abcdef"))
	fmt.Println(Capitalize("hi there"))
	fmt.Println(Capitalize("CAPITALIZED"))

	// Output:
	// Abcdef
	// Hi There
	// Capitalized
}