Skip to content
On this page

Importing libraries, with and without a collision-preventing alias

Go, singular import of a full libraries

package main

import "fmt"

func main() {
	fmt.Println("Hello printf")
}

JS, CJS, any variant of full-library

const anyname = require('util');

JS, ESM, any variant of full-library

import util from 'node:util';

Go, multiple libraries

package main

import (
	"fmt"
	"time"
	"math"
	"math/rand" // convention is: the files providing that say `package rand`
)

// (individual import statements are legal but not recommended)

func main() {
	fmt.Println("Hello", time.Now(), math.Pi, math.Sqrt(4), rand.Intn(10))
}

Go aliasing a naming conflict

https://go.dev/ref/spec#Import_declarations

import (
	localname "package_name"
)

Exporting a function

Go exports Capitalized functions

package lib

func Thisexports() {
}

func this_doesnt() {
}

Js, CJS

module.exports = {f};
function f() {}

Js, ESM

export function f() {}

Parameter Types

Go is like C-reversed

https://go.dev/blog/declaration-syntax

func f(a int, b int) int {
	return a+b
}

For a series of the same type, you can omit all but the last:

func point3d(x, y, z int) {
}

Tuples are valid types:

func indecisive(a string, b string) (string, string) {
	return (
		fmt.Sprint("I like ", a),
		fmt.Sprint("but I also like ", b),
	)
}
func main() {
	a, b := indecisive("cats", "dogs")
}

(fmt.Sprint() adds spaces if neither adjacent term is a string)

Js has only universal refs

function f(a, b) {
	return a+b;
}
const fatarrow_statement_body = (a, b) => {return a+b;};
const fatarrow_expression_body = (a, b) => a+b;

Ts is like Go but with a colon

https://www.typescriptlang.org/docs/handbook/2/functions.html

function f(a: number, b: number) {
	return a+b;
}

For functions as parameters, inline gets unreadable quickly:

function b(adder: (a: number, b: number) => number) {
	return adder(1,2);
}

But type aliases are nice:

type BinaryOpFunc = (a: number, b: number) => number;
function b(adder: BinaryOpFunc) {}

Return statements

Go likes naming returns at the top

If you put a name on your return signature elements, you can make a naked return statement return those locals:

func returnsTuple() (x, y int) {
	x = 12
	y = 42
	return // "(x, y)" is implied
}

func main() {
	x, y := returnsTuple()
}

Js and everyone else wouldn't

function returnsArray() {
	return [12, 42];
}
function main() {
	const [a,b] = returnsArray();
}

Returns are not Tuples

I've been reading return a,err as if the func returns a tuple, but that's not exactly true. If it were a tuple, it'd be one lvalue or parameter when consumed, but here, the call to CommaErr() unpacks the sequence as two actual params:

package main

import "fmt"

func main() {
	TestCheck()
	TestCommaErr()
}

func Check(err error) {
	if err != nil {
		panic(err)
	}
}

func CommaErr[T any](v T, err error) T {
	if err != nil {
		panic(err)
	}
	return v
}

func Parse(str string) (i int, err error) {
	if str == "42" {
		i = 42
	} else {
		err = fmt.Errorf("cannot parse %v", i)
	}
	return
}

func Ping() error {
	return fmt.Errorf("nothing to ping")
}

func recovery() {
	if err := recover(); err != nil {
		fmt.Println("recover said", err)
	}
}

func TestCheck() {
	defer recovery()
	Check(Ping())
}

func TestCommaErr() {
	defer recovery()
	fmt.Println("test42", CommaErr(Parse("42"))) // passes; CommaErr returns int(42)
	fmt.Println("test20", CommaErr(Parse("20"))) // works as designed: CommaErr panics with Parse's error.
}

Variable Declarations

Go uses var and a separate type

func main() {
	var name string // without an initial value, decl must have a type
	var job = "pope"
	job := "pope" // _inside a function but not at top scope,_ pascal-assign is shorthand for "var {name}={val}"

	var x,y,z int; // just like in signatures, repetitive types can be collapsed.

	var x,y,z int = 1,2,3; // Go list-decls have a typle-based initializer syntax

	const a = 42; // sounds limited to char, string, bool, and numbers? really?

	// consts are compile-type _values_ without a firm type, so this works without a cast:
	b int8 := a
	c float32 := a
}

Js only uses runtime type info

function main() {
	var old_fashioned_let; // don't use var. var has peculiar scoping rules and should be left in the trashbin with the other early-century rubbish.
	const this = "is a constant pointer/ref to unconstant data";
	let normal_var;

	const x=1, y=2, z=3; // JS initializer syntax are based on comma delimited expressions.
}

Fundamental Types

JsGo
stringstring
booleanbool
numberint int8 int16 int32 (alias: "rune" for unicode reasons) int64
uint uint8 (alias: byte) uint16 uint32 uint64 uintptr float32 float64
BigInt
complex64 complex128
null
undefined
symbol
  • Like C/C++, the width of int, uint, and uintptr are architecture-specific. So don't use them for portable numbers; they are for tasks that are supposed to be locked to machine width.
  • Go decls without an initializer are initialized to "zeroish": empty for string, false for bool, nil for pointers, zero for everything else.
  • Js refs initialize to undefined.
  • Casting: Go is like C++'s explicit casts: i:=42; var f float = float(i)

Example of complex128:

import (
	"fmt"
	"math/cmplx"
)

var z complex128 = cmplx.Sqrt(-5 + 12i)

func main() {
	fmt.Printf("Type: %T Value: %v\n", z, z)
}

result: Type: complex128 Value: (2+3i)

Strings

Interpolation

see package "text/template"

https://go.dev/blog/stringshttps://www.reddit.com/r/golang/comments/153g5k6/easier_string_interpolation/https://github.com/golang/go/issues/34174https://github.com/golang/go/issues/34174#issuecomment-1450932232https://stackoverflow.com/questions/53879154/println-vs-printf-vs-print-in-go

Concat

import (
	"fmt"
	"strings"
)

func main() {
	list := []string{"a", "b", "c"}
	fmt.Println(strings.Join(list, ",")) // result "a,b,c"
}

In Js:

function main() {
	const list = ['a', 'b', 'c'];
	console.log(list.join(','));
}

For loops, switches, and conditionals

Go has no parens and no brace-less mode

func main() {
	if a < b {
		doStuff()
	}

	for i := 0; i < 10; ++i {
		fmt.Print(i)
	}

	// can leave init/inc out:
	i := 0
	for i < 10 {
		i += 1
	}

	// or "while(true) {}" with no terms:
	for {
		doForever();
	}

	// switch statements GET AN AUTOMATIC BREAK.
	switch "a"+"b" {
	case "ab":
		fmt.Println("I run")
	case "cd":
		fmt.Println("I do not")
	default:
		fmt.Println("not a chance.")
	}
}
  • Switch statements can also have a prelude statement like ifs described below.
  • Also, switch cases don't have to be constants, and the switch-eval short circuits before evaluating unnecessary case-expressions.
  • Given that, they also added switch { to mean switch true { so you can use this as a funny "if/ifelse*/else" equivalent:
t := time.Now()
	hr := t.Hour()
	switch {
	case hr <12:
		t = "morning"
	case hr <17:
		t = "afternoon"
	default:
		t = "evening"
	}

Js

function main() {
	if (a < b) doStuff();

	for (let i = 0; i < 10; ++i) console.log(i);

	for (let i = 0; i < 10; ++i) {
		console.log(i);
	}
}

Js-switches also short circuit:

const a = 42;

switch (a) {
	case guess() - 1:
		console.log("yay -1");
		break;
	case guess():
		console.log("yay +0");
		break;
	case guess() + 1:
		console.log("yay +1");
		break;
}

function guess() {
	console.log("guessing");
	return 42;
}

results in two calls to guess()

Go conditional-scoped vars

You can semi-colon prepend a statement into the conditional test clause; any vars created have the scope of the conditional body:

func main() {
	if a := 42; a < 50 {
		fmt.Println(a, " is less than 50")
	} else {
		// still accessible here
		fmt.Println(a, " is more than or equal to 50")
	}
	// "a" is _not_ accessible here
}

The equivalent in modern Js resembles C++ scoping:

function main() {
	{ // create an extra scope
		const a = 42;
		if (a < 50) console.log(`${a} is less than 50`);
	}

	// but both languages support this in conditionals, where it's been the norm for decades. See above for loops.
}

Deferring cleanup

Go defer adds exit cleanup

Other than bash's exit traps, I'm only used to C-style. But Go has function-scope cleanup support:

func closeFile(handle) {
	fmt.Println("closing", handle.filename)
}

func main() {
	file := openFile('foo')
	defer closeFile(file)
	// do work with file, knowing close will be called at main-exit. (even during panic)
}

A case can be made that Go should have made this block scope, like later adopters did.

Js requires more manual handling of custom cleanup

function main() {
	let file;
	try {
		file = openFileSync('foo');
		// do work.
	} catch (e) {
		closeFile(file);
		throw e;
	}
	closeFile(file);
}

C++ still wins on this one

in C++, I'd just use a smart pointer and a destructor.

Pointers: Go is "C w/o allowing pointer math"

func ptrmuckery() {
	i := 42
	pointer_to_i = &i
	*pointer_to_i = 24
}

func setThisPtr(val int, dest *int) {
	*dest = int
}

func main() {
	var i int
	setThisPtr(42, &i)
}

Js has pointers hiding in plain sight as every var is a reference. But you can't pass a pointer to a pointer syntactically, so the closest you could get to an output-formal-parameter-ref is to create an object:

function setThisPtr(val, dumb_handle) {
	dumb_handle.ptr = val;
}
function main() {
	const handle = {}; // I don't even _have_ to declare ptr if I don't want to.
	setThisPtr(42, handle);
	console.log(handle.ptr);
}

Structs

type Coord3 struct {
	x,y,z float32
}
func main() {
	fmt.Println(Coord3{1,2,3}) // result: `{1 2 3}`

	// named terms also work, and unset fields follow the zeroish initialization rule:
	other := Coord3{y: 42}
	fmt.Println(other) // result: `{0 42 0}`
}

Js: everything is (or inherits from) POJO:

function main() {
	const home = {x: 1, y: 2, z: 3};
	console.log(home); // results: `{ x: 1, y: 2, z: 3 }`
}

// though there's a nice syntactic sugar that makes it look more OOP:

class Coord3 {
	constructor(x, y, z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
}

function main() {
	const home = new Coord3(1,2,3);
	console.log(home); // results: `Coord3 { x: 1, y: 2, z: 3 }`
}

Struct fields

In all languages I'm considering here, member access is simply "obj.member."

Go has an oddity for convenience: dereferecing a pointer to get at a member is also just a dot, unlike C's ->.

func main() {
	c := Coord3{1,2,3}
	p := &c
	c.x, (*p).x and p.x are the same thing.
}

Contrast: in C++ that would be p->x and since Js only has references, dot is technically always an indirection.

OOP-ish

package main

import (
	"errors"
	"fmt"
)

type GenericAccount interface {
	Deposit(amount float64)
	Withdraw(amount float64) error
}

type BankAccount struct {
	balance float64
}

func (this *BankAccount) Deposit(amount float64) {
	if this == nil {
		fmt.Printf("BankAccount::Deposit(%v) called on a nil ptr\n", amount)
		return
	}
	this.balance += amount
}

func (this *BankAccount) Accrue(percent float64) {
	if this == nil {
		fmt.Printf("BankAccount::Accrue(%v) called on a nil ptr\n", percent)
		return
	}
	this.balance *= 1+percent
}

func (this *BankAccount) Withdraw(amount float64) error {
	if this == nil {
		return fmt.Errorf("BankAccount::Withdraw(%v) called on a nil ptr\n", amount)
	}
	if amount <= 0 {
		// errors.New() is best suited for creating static, simple error messages that do not require dynamic content or wrapping other errors.
		// For more complex error messages or error wrapping, fmt.Errorf() is often preferred.
		return errors.New("can't be negative")
	}
	if this.balance < amount {
		return errors.New("not enough to make withdrawal")
	}
	this.balance -= amount
	return nil // if there are error returns, you can't leave the last one implicit.
}

func main() {
	var real_account = BankAccount{balance: 100}
	// since the methods are mutators, they're pointer-receivers.
	// Because of that, it's only "*BankAccount" that implements GenericAccount, not plain value "BankAccount".
	var acct GenericAccount = &real_account

	// instance of interface ref will call the right concrete method
	if e := acct.Withdraw(50); e != nil {
		fmt.Println("error:", e)
	}
	fmt.Println("should be 50:", real_account.balance)

	// but you have to dynamic cast to get at a leaf-only bit like Accrue(), and you can't cast to a value or it'll fail the interface's spec.
	if ptr, ok := acct.(*BankAccount); ok {
		ptr.Accrue(.1)
		fmt.Println("should be ~55:", real_account.balance)
	} else {
		fmt.Println("we'd get here if it wasn't this concrete type")

	}
}

Stringer String() and controlling fmt's pretty print

Anything implementing String() string will be noticed by fmt:

type Acct struct {
	Email string
	Pass string
	Name string
	Age  int
}
func (this Acct) String() string {
	// return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
	this.Pass = "[redacted]" // since this method is a value-receiver, we're not mutating the main copy
	str, _ := json.Marshal(this)
	return str
}

Errors

errors.New(str) can be used for trivial strings, but if you're formatting or wrapping, may as well use the newer Errorf.

The %w specifier sets up error wrapping:

if sproing, err := func(); err != nil {
	return nil, fmt.Errorf("could not func: %w", err)
}
}

You can test whether an error class is anywhere in the wrap-chain:

if errors.Is(err, package.ErrorClass) {
}

If you need the structure of an error class, the unwrapping cast-equivalent is:

var pErrorClass *package.ErrorClass // As() needs a pointer as an output ref
if errors.As(err, &pErrorClass) {
	// use pErrorClass
}

Idiomatic

var (
	ErrFubar = errors.New("first lib error")
)
func LibDo() {
	return nil, ErrMyPackageErrar
	// or
	return nil, fmt.Errorf("stuff happened: %w", ErrMyPackageErrar)
}

and in the caller

func caller() {
	derp, err := lib.LibDo()
	if errors.Is(err, lib.ErrFubar) {
		// handle case
	}
}

An example of a big switch of errors.As()

Custom

When your func declares a return type of "error", that's a built-in interface:

type error interface {
    Error() string
}

So you can define a custom error classish struct by implementing it:

import (
	"fmt"
	"os"
)

type PebkacError struct {
	Who string
	Keystrokes string
}
func (this PebkacError) Error() string {
	return fmt.Sprintf("it's %v's fault because they pressed %v", this.Who, this.Keystrokes)
}

func fallableFunc() error {
	return PebkacError{"Bob", "roflbbq"}
}

func main() {
	if err := fallableFunc(); err != nil {
		fmt.Println("FAIL:", err)
		os.Exit(1)
	}
}

And if your custom class implements either Unwrap() error or Unwrap() []error it must return nil (meaning "I'm the innermost error") or the inner error(s). (The array form is because errors.Join() does it that way; you can also have multiple "%w" in Errorf().)

Returnval Interception

Naming returns in the function sig isn't just a documentation thing: given a func with named returnvals, you can replace them on the way out with defer:

package main

import (
	"fmt"
	"errors"
)

func indecisive(question string) (resp string, err error) {
	defer func() {
		resp = "no! wait! yellow!"
		err = errors.New("can't decide")
	}()
	switch (question) {
		case "what... is your favorite color?":
			resp = "blue!"
		default:
			resp = "I don't know"
	}
	return // mandatory, but implictly uses locals declared in the retval sig.
}

func main() {
	if ans, err := indecisive("what... is your favorite color?"); err == nil {
		fmt.Println("ans:", ans);
	} else {
		fmt.Println("err:", err, "answer given was:", ans);
	}
}

Time

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println(time.Date(1933, 11, 22, 12, 34, 56, 78, time.UTC)) // 1933-11-22 12:34:56.000000078 +0000 UTC

	start := time.Now()
	tick := time.Tick(100 * time.Millisecond)
	boom := time.After(500 * time.Millisecond)
	elapsed := func() time.Duration {
		return time.Since(start).Round(time.Millisecond)
	}
	for {
		select {
		case <-tick:
			fmt.Printf("[%6s] tick.\n", elapsed())
		case <-boom:
			fmt.Printf("[%6s] BOOM!\n", elapsed())
			return
		default:
			fmt.Printf("[%6s]     .\n", elapsed())
			time.Sleep(50 * time.Millisecond)
		}
	}
}

generics

func MyFunc[Name rule, Name2 rule](var Name) Name {
	return var
}

Rules:

  1. comparable is a built-in constraint tag that means "== and != are allowed"
  2. the rule can be a type like int64 or a union of them: int64 | uint64
  3. you can make your own type constraint:
    type Number interface {
    	int64 | float64
    }
    func MyFunc[T Number](var T) {}
  4. You can add methods to a generic, but not a generic method.
  5. You can "overload" to take "string | *string" but it's ugly:
package main

import "fmt"

type Addable interface {
    int | int8 | int16 | int32 | int64 | float32 |
    uint | uint8 | uint16 | uint32 | uint64 | float64 |
    string
}


func Sum[T Addable](nums ...T) T {
    var sum T
    for _, v := range nums {
        sum += v
    }
    return sum
}

type List[T comparable] struct {
    list []T
}
func (this *List[T]) indexOf(ref interface{}) int {
	switch specific := ref.(type) {
	case T:
		return this.indexOf(&specific)
	case *T:
		for i, v := range this.list {
			if v == *specific {
				return i
			}
		}
	default:
		fmt.Println("didn't get a good type", ref)
	}
    return -1
}

func main() {
	total := Sum(1,2,3)
	fmt.Println("total:", total)

	cats := List[string]{[]string{"Chaos","Soot","Spaz"}}
	// testing *string, which was my original simple implementation:
	ref := "Soot"
	fmt.Println("should be 1:", cats.indexOf(&ref))
	ref = "Monkey"
	fmt.Println("should be -1:", cats.indexOf(&ref))

	// testing "also takes string by value"
	fmt.Println("should be 2:", cats.indexOf("Spaz"))
}

Arrays and Slices

import (
	"encoding/json"
	"fmt"
)

func main() {
	// Go arrays have a unique declaration order, but after that are normal
	var ten_ints [10]int // arrays are immutable and the length is technically part of the type.
	ten_ints[0] = 5

	// array literals have curlies, just like structs
	luggage_password := [5]int{1,2,3,4,5}
	// a constructor w/o length is mostly the same, but you're technically constructing a _slice_ not an array
	luggage_password := []int{1,2,3,4,5}
	// to auto-length a real array, use ellipsis (https://rtbell.dev/blog/golang/three-dots)
	luggage_password := [...]int{1,2,3,4,5}

	// slicing into a list-like with a pythonesque syntax:
	subset := luggage_password[1:2]
	// this results in a REF TO ORIGINALS; go slices are documented as not copying anything.

	// which is end-exclusive zero based:
	json,_ := json.Marshal(subset)
	fmt.Printf("json string was %s\n", json) // result: [2], because it starts at offset 1 and ENDS-BEFORE offset 2.

	// unspecified terms in a slicer default to "zero" and "array length"
	// luggage_password[2:] VS luggage_password[:2] VS luggage_password[:]
}

Js Arrays are more like Go slices, as those are the two mutable ones.

function main() {
	const luggage_password = [1,2,3,4,5];
	const subset = luggage_password.slice(1,2); // N.B! SLICE is like go slicing; array.splice is a mutator with an unrelated signature!
	console.log(subset); // [2]
}

Slices can re-acquire underlying elements

Since a go slice is just a window-reference to the underlying array, go exposes a way to move the endpoints by copy-constructor:

package main

import (
	"fmt"
)

func main() {
	luggage_password := [5]int{1,2,3,4,5}
	temp := luggage_password[1:3]
	fmt.Println("full array:", luggage_password, "sliced to [1:3]:", temp)
	fmt.Println("len() respects the current end marker:", temp, "has len()", len(temp))
	fmt.Println("cap() reports on currently unused elements:", temp, "has cap()", cap(temp))

	temp = temp[:cap(temp)] // [:] respects the current length of the slice, so to grab all out-of-window elements...

	fmt.Println("after extension, len() is", len(temp), "because" temp)

	// you can't back a slice up past its current zero-point; trying [-1:] just errors with "invalid argument: index -1 (constant of type int) must not be negative"

	// while an empty slice on an array is not nil, a slice without a foundation is nil testable:
	var nil_slice []int
	fmt.Println(nil_slice, len(nil_slice), cap(nil_slice), nil_slice == nil) // results: [] 0 0 true
	empty_slice := luggage_password[len(luggage_password):len(luggage_password)]
	fmt.Println(empty_slice, len(empty_slice), cap(empty_slice), empty_slice == nil) // results: [] 0 0 false

	// Iterating a slice using for-range. (Also works on maps.)
	for index, val := range luggage_password {
		fmt.Println("offset", index, "is", val)
	}

	// don't need index?
	for _, val := range luggage_password {
		fmt.Println(val)
	}

	// don't need value in the loop control itself?
	// unlike a func like Marshal()'s returnval of {bytes[], error?}, a for-range statement can work without terminal dummy "_"
	for index := range luggage_password {
		fmt.Println("testing offset", index)
	}
}

Js iterations:

function main() {
	// notice "for-in" vs "for-of"!

	// POJOs as MAPS

	const a_map = {a: 'val', another: 'value'};
	for (const key in a_map) {
		const val = a_map[key];
		console.log({val, key});
	}

	// before the days of for-in, we had Object.entries() and its friends. Still valid but I wouldn't dupe a large dataset in perf-sensitive code.
	for (const [key, val] of Object.entries(a_map)) {
		console.log({val, key});
	}

	// ARRAYS

	const a_list = [1,2,3,4,5];
	for (const val of a_list) {
		console.log("for-of a list", val);
	}
	// or if you want the index as well, use the primordial forms:
	for (let i = 0; i < a_list.length; ++i) {
		const val = a_list[i];
		console.log({i, val});
	}
	a_list.forEach((val, i) => console.log({i, val}));
}

Using Slices as Dynamic Arrays

func main() {
	// a convenience for making what another lang would call a dynamically sized array, initialized to zeroish:
	// make(${SLICE TYPE}, ${len}, ${optional_capacity_parameter}) // cap defaults to len
	nums := make([]int, 5, 10)

	// WARN: append reserves the right to allocate a new array if space runs out, so it MAY mutate original's backing array.
	// append(${orig_array}, ${more_elements...})

	more_nums := append(nums, 12) // since spare cap was allocated in make(), the backing array for more_nums is the same as nums.
}

go:append() and Go Slices: usage and internals

Nesting: a literal list of structs

func main() {
	// an array of structs initialized in the decl looks like:
	// []struct {def} {initializers}
	// note that def is comma-free but initializers is comma delimited and comma terminated if multiline. Yay consistency!

	pairs := [] struct {
		name string
		age int
	} {
		{"bob", 42},
		{"sue", 24},
		{age: 100},
	}
	fmt.Println(pairs)
}

Maps

https://go.dev/tour/moretypes/20https://gobyexample.com/mapshttps://stackoverflow.com/questions/47579004/what-can-be-used-in-a-map-literal-instead-of-a-type-name-in-go

// map[KeyType]ValueType{Key1: Value1, Key2: Value2, ...}

menu := map[string]float64{
	"eggs":    1.75,
	"bacon":   3.22,
	"sausage": 1.89,
}

type Coord struct {
	x, y float32
}

var global_places = map[string]Coord {
	"house": Coord{1,2},

	// if the map-keytype is fixed, you can shorten the literal:
	"other": {3,4},
}

func main() {
	// "make()" can create empty maps
	places := make(map[string]Coord)
	places["home"] = Coord{1,2}
	fmt.Println(places)

	// delete() to delete an entry
	delete(places, "home")

	// an exist test is hidden on the getter:

	_, home_exists_bool := places["home"]
}

Variadic functions

import "fmt"

var verbose = true

func dbgPrint(stuff ...any) {
	if (verbose) {
		fmt.Println(stuff...)
	}
}

func main() {
	dbgPrint("hello world")
}

Passing Functions and Closures

You could inline all your functypes, but a typedef is much more readable

func compute_inlined(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

type FloatyOp func(float64, float64) float64

func compute_typed(fn FloatyOp) float64 {
	return fn(3, 4)
}

func main() {
	salt := 3
	summer := func(a, b float64) float64 {
		return a+b + salt;
	}
	if compute_typed(summer) == 10 {
		fmt.Println("pass")
	}
}

Typecasting

package main

import "fmt"

type Person struct {
	name string
	age  int
}

type Robot struct {
	serial int
	mission string
}

func main() {
	var p interface{}

	// Static-ish casts:
	var x int = 42
	x2 := uint8(x)
	fmt.Println(x, "as byte:", x2)

	// dynamic casts work on interface{}s, not primitives:
	// so you can't say
	// x3 := x.(string)
	// fmt.Println(x, "as string:", x3)

	// panics if error is not explicitly caught:
	if false {
		p = x
		x3 := p.(string) // THROWS FATAL `panic: interface conversion: interface {} is int, not string`
		fmt.Println(p, "as string:", x3)
	}

	// catching the ok/error output prevents panics:
	x4, was_stringable := p.(string)
	fmt.Println(x, "is string?", was_stringable)
	fmt.Printf("typeof x4=%T but len()=%d\n", x4, len(x4))

	p = Person{name: "Alice", age: 25}

	// would not work because "interface{}" has no fields
	// fmt.Println(p.name)
	// fmt.Println(p.age)

	// if I activate this, all raw Person casts would fail with "panic: interface conversion: interface {} is main.Robot, not main.Person"
	if false {
		p = Robot{42, "murder"}
	} else {
		// so cast p inline:
		fmt.Println(p.(Person).name)

		p2 := p.(Person) // or hold the ref
		fmt.Println(p2.age)
	}

	// conversely, I can trigger the default block: and it'll say "type unknown: int"
	if false {
		p = 42
	}

	// using the prelude statement trick that statements like "if" support provides a succinct (if ugly) way to fork on dynamic casts:
	if person, valid := p.(Person); valid {
		fmt.Printf("Person path: is a %T\n", person) // %T says "main.Person"
		fmt.Println(person.name)
		fmt.Println(person.age)
	} else if robot, valid := p.(Robot); valid {
		fmt.Printf("Robot path: is a %T\n", robot) // %T says "main.Robot"
		fmt.Println("object:", robot) // {42 murder}
	} else {
		fmt.Printf("type unknown: %T\n", p)
	}

	// though using the keyword 'type' is more elegant than the above:
	switch actual := p.(type) {
		case Robot:
			fmt.Println("switch says robot", actual)
		case Person:
			fmt.Println("switch says person")
	}
}

JSON

https://go.dev/blog/jsonhttps://gobyexample.com/jsonhttps://pkg.go.dev/encoding/json#Marshalhttps://pkg.go.dev/encoding/json#Unmarshalhttps://pkg.go.dev/encoding/json#RawMessage

sanity checking hooks will be called by dummy-dynamic-casting to Marshaller https://go.dev/doc/effective_go#blank_implements

Dear god. I should check out http://gregtrowbridge.com/golang-json-serialization-with-interfaces/ which was followed up by https://www.brimdata.io/blog/unmarshal-interface/ also sounds like vets talking: https://www.reddit.com/r/golang/comments/b7xgsp/consuming_unknown_json_fields/https://stackoverflow.com/questions/63913044/json-stringify-equivalent-in-golang-for-mapstringinterface reflect -> https://stackoverflow.com/questions/20170275/how-to-find-the-type-of-an-object-in-go extract reflection from this mess: https://www.accuweaver.com/2024/02/10/delighted-to-resolve-unexpected-consequences-in-go-json-marshal/

import "reflect"
func main() {
	str := reflect.TypeOf([]int{1,2,3})
}

https://boldlygo.tech/posts/2019-12-19-go-json-tricks-array-as-struct/ (json and http) https://blog.boot.dev/golang/json-golang/

a nasty glitch from naive customization of marshalling hooks https://www.crowdstrike.com/en-us/blog/unexpected-adventures-in-json-marshaling/https://medium.com/@chaewonkong/go-and-json-a-comprehensive-guide-to-working-with-json-in-golang-143fa2dfa897

package main

import (
	"encoding/json"
	"fmt"
)

// ALTERNATIVELY, skip the tags and just use go-fields that are case-insensitive-matches for the JSON.
type CoverageExample struct {
	Str  string  `json:"stringValue"`
	Num  float32 `json:"numberValue"`
	Int  int     `json:"integerValue"`
	Bt   bool    `json:"booleanTrueValue"`
	Bf   bool    `json:"booleanFalseValue"`
	Null any     `json:"nullValue"`
	Arr  []any   `json:"arrayValue"`
	Obj  struct {
		Str string  `json:"nestedString"`
		Num float32 `json:"nestedNumber"`
	} `json:"objectValue"`
}

var sample []byte = []byte(`
	{
		"stringValue": "This is a string",
		"numberValue": 123.45,
		"integerValue": 42,
		"booleanTrueValue": true,
		"booleanFalseValue": false,
		"nullValue": null,
		"objectValue": {
			"nestedString": "Another string",
			"nestedNumber": 99
		},
		"arrayValue": [
			"first element",
			2,
			true,
			null,
			{ "arrayObject": "inside array" },
			[ "nested", "array" ]
		]
	}
`)

func main() {
	var data CoverageExample
	err := json.Unmarshal(sample, &data)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", data)

	bytes, err := json.Marshal(data)
	if err != nil {
		panic(err)
	}
	fmt.Printf("simply stringed: %#s\n", bytes)

	bytes, err = json.MarshalIndent(data, "", "\t")
	if err != nil {
		panic(err)
	}
	fmt.Printf("prettyprinted:%#s\n", bytes)

	bytes, err = json.MarshalIndent(data, ">", "\t")
	if err != nil {
		panic(err)
	}
	// note that I manually added the first line's prefix here:
	fmt.Printf("example of prefix field: \n>%#s\n", bytes)
}

https://stackoverflow.com/questions/36224779/golang-json-struct-to-lowercase-doesnt-work?rq=3https://stackoverflow.com/questions/28644600/how-to-json-decode-lowercased-names-into-my-struct?rq=3https://stackoverflow.com/questions/24837432/capitals-in-struct-fields

Decoding Unknown JSON

If you don't have a struct compiled and need to inspect, you'll need to jump through any-ish hoops:

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

func DecodeStrangeJSON(bytes []byte) (out map[string]any, err error) {
	err = json.Unmarshal(bytes, &out)
	return
}
func DecodeJSON[Serializable any](bytes []byte) (out Serializable, err error) {
	err = json.Unmarshal(bytes, &out)
	return
}

type CoverageExample struct {
	Str string `json:"stringValue"`
	Num float32 `json:"numberValue"`
	Int int `json:"integerValue"`
	Bt bool `json:"booleanTrueValue"`
	Bf bool `json:"booleanFalseValue"`
	Null any `json:"nullValue"`
	Obj struct {
		Str string `json:"nestedString"`
		Num float32 `json:"nestedNumber"`
	} `json:"objectValue"`
	Arr []any `json:"arrayValue"`
}

func main() {
	sample := []byte(`
	{
		"stringValue": "This is a string",
		"numberValue": 123.45,
		"integerValue": 42,
		"booleanTrueValue": true,
		"booleanFalseValue": false,
		"nullValue": null,
		"objectValue": {
			"nestedString": "Another string",
			"nestedNumber": 99
		},
		"arrayValue": [
			"first element",
			2,
			true,
			null,
			{ "arrayObject": "inside array" },
			[ "nested", "array" ]
		]
	}
	`)
	fmt.Printf("sample: %s\n", sample)
	raw, err := DecodeStrangeJSON(sample)
	if err != nil {
		panic(err)
	}
	for k, v := range raw {
		fmt.Printf("top level [%s]<%T> = %v\n", k, v, v);
	}

	structured, err := DecodeJSON[CoverageExample](sample)
	if err != nil {
		panic(err)
	}
	fmt.Printf("structured? %#v\n", structured)
	/*for k, v := range structured {
		fmt.Printf("top level [%s]<%T> = %v\n", k, v, v);
	}
	*/
	stru := reflect.ValueOf(structured) // we're working with the concrete side of the ptr, hence "value"
	if stru.Kind() != reflect.Struct {
		panic("this is a constant in the sample, just illustrating how to chekc")
	}
	ShowKeysInStruct("structured", structured)
	return
	for i := 0; i < stru.NumField(); i++ {
		field := stru.Type().Field(i)
		fmt.Println(field) // Print the field name
		ShowKeysInStruct("field", field)
	}
}

func ShowKeysInStruct(name string, thing any) {
	meta := reflect.ValueOf(thing)
	k := meta.Kind()
	switch k {
	case reflect.Struct:
		for i := 0; i < meta.NumField(); i++ {
			field := meta.Type().Field(i)
			other := meta.Field(i)
			fmt.Printf("%s.%s<%v> = %#v\n", name, field.Name, other.Kind(), other)
		}
	default:
		fmt.Printf("%s is a %v (%T)\n", name, k, thing)
	}
}

XML

https://gobyexample.com/xml

HTTP RESTful

usage of the built-in: https://www.alexedwards.net/blog/an-introduction-to-handlers-and-servemuxes-in-gohttps://dev.to/leapcell/gos-httpservemux-is-all-you-need-1mamhttps://leapcell.medium.com/gos-http-servemux-is-all-you-need-f33ad63ed2b1https://shijuvar.medium.com/building-rest-apis-with-go-1-22-http-servemux-2115f242f02bhttps://eli.thegreenplace.net/2023/better-http-server-routing-in-go-122https://www.kelche.co/blog/go/http-server/#:~:text=This server includes essential features,logging for debugging and monitoring

Making Calls into C

package main

// using a magic docstring: it can contain literal declarations, or linker flags for the cgo tool like
// #cgo LDFLAGS: -lm

/*
#include <stdio.h>
#include "your_custom_code.h"
*/
import "C"

func main() {
	C.puts(C.CString("Hello from C called by Go!"))
}

It is also possible to expose Go functions as a C shared library or static library that can be called from C code. This involves building your Go package with the c-shared or c-archive build modes, which generate a C-compatible library and header file.

https://www.reddit.com/r/golang/comments/ayhql4/using_c_libraries_in_go/https://www.thegoldfish.org/2019/04/using-c-libraries-from-go/https://dev.to/metal3d/understand-how-to-use-c-libraries-in-go-with-cgo-3dbnhttps://github.com/lxwagn/using-go-with-c-libraries

Concurrency

dirty lifehack: an empty select {} will block forever but at least not burn cycles.

sync atomic chan

https://gobyexample.com/select

Receiving from a nil channel blocks forever. Receiving from a closed channel always succeeds, immediately returning the element type's zero value.

close(chan)

If you're not checking the ok, you'll get zeroish forever.

WaitGroups

Don't count goroutines manually, use the official class manually to manually increment and decrement your count.

https://gobyexample.com/waitgroups

Also, there's one that reaps the first error returned from any child goroutine: https://pkg.go.dev/golang.org/x/sync/errgroup

func naiveHelper() {
	// do work
}

import "sync"

type Pool struct {
	adder func()
	sync.WaitGroup // embedded so you can say Pool.Wait
}
func (this *Pool) Add() {
	this.Add(1) // inc the wg
	go this.wrapper()
}
func (this *Pool) wrapper() {
}

func RunPool(launcherFunc func()) {
}

func main() {
	wg := sync.WaitGroup // this example never passed it around, but be sure to pass by pointer to avoid mutating copies.



}

Letting the goroutine die even if never read

(This can come up if the reader has a timeout, so it may give up before reading you.)

Make the chan buffer 1; the final answer can be pushed without the goroutine pausing before teardown.

ref: https://gobyexample.com/timeouts

nonblocking is easy: https://gobyexample.com/non-blocking-channel-operations

giving up slice w/o reading a chan

runtime.Gosched

Channels

Select and reflect.Select

Reflection

Embed Directive

gRPC and Protobuf

https://protobuf.dev/best-practices/

in JS

  1. official: npm(@grpc/grpc-js) https://grpc.io/docs/languages/node/basics/
  2. official: npm(grpc-web) https://github.com/grpc/grpc-web
    • POS does not support duplex streams OR WORK W/O A PROTOCOL PROXY
    • wait, the hello world for grpc-web just uses @grpc/grpc-js anyway!
  3. recommended by postman
  4. same lib as postmans, but w/ cute TS decorator? https://docs.nestjs.com/microservices/grpc
  5. AWS-heavy transcriber https://subaud.io/blog/node-grpc-server
  6. https://github.com/grpc-ecosystem/awesome-grpc?tab=readme-ov-file#lang-nodejs
  7. one candidate is https://github.com/connectrpc/connect-es

Other notes

  1. Go doesn't want to work with shebang. Others have cobbled solutions but the sane answer is to wrap it instead of trying to get it to act like a bash script file.
  2. fancy (and boilerplate heavy) replacement for named args: https://uptrace.dev/blog/golang-functional-options
  3. network timeouts and naive context deadlines

JavaScript/Bash code released under the MIT License.