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.