Golang functions
Golang has first class functions, you can pass functions as parameters.
Function Signatures
Function with arguments, return value
// (param type) (return-type) func greet(name string) string { return "Hello, " + name }Multiple return values
// when multiple return values present, surround with brackets func find(id int) (string, int) { // ... } // optionally, return values can be named // (this has no implications for caller, it's simply documentation) func find(id int) (name string, age int) { // 'values' is array ... }Combine params with same type
// params 'firstname', 'lastname' both have type 'string' func register(firstname, lastname string) { // ... }Param values vs pointers
// if 'name' param changes in func, does not change outside func register immutable(name string) { ... } // if 'name' param changes in func, modifies value in memory // (more efficient, does not copy data to new memory location) func register_mutable(*name string) { ... }Variadic parameters (variable number of params)
func sum(names ...string) { // 'names' is a slice of 'string's new_names = append([]int{alex}, names...) // prepend to names slice }Returning pointers
// even if variables are declared on the stack, // you can safely return a pointer to them // (as lomg as your return value is a pointer) func DoThing() *int { var foo *int = &123 return foo }Optional parameters
// all parameters are optional! func DoThing(a string, b int) { fmt.Printf("'%s', '%s'", a, b) return } DoThing() // "'', ''"Default parameter values
// no syntactic sugar for defaults, but can handle inside function func SayHello(name string) { if name == "" { name = "guest" } fmt.Printf("Hello %s", name) }Functions as Parameters (TESTME!)
func Visit(msg string, visitor func(string) string) string { return visitor(msg) } func Visitor(msg string) string { fmt.Println(msg) } // you can pass in a function Visit("abc", Visitor) // you can also define functions ad-hoc Visit("abc", func(msg string) string { })
Generics
Generic functions can accept params for a range of types.
// this method accepts either an int/float for both 'a' and 'b' params // (we define value for 'NUM', then reuse it) func Sum[NUM int | float64](a NUM, b NUM) NUM { return a + b }
Deferred functions
Defer waits until a function is just about to exit (even on failure).
This is similar to a try/finally block in other languages.
Docs describe it as useful for releasing a mutex, for example.func WriteFile(filepath string) (success int) { fd, err := os.Open(filepath) defer fd.Close() // run before function closes // ... other code ... }
- Deferred functions are executed in the order of last-in-first-out (LIFO).
- Panics evaluate after deferred functions
Variables passed to a defferred function retain their value at the time the function call was made.
func main() { a := "start" defer fmt.Println(a) b := "end" } // "start" <-- 'a' at time of defer was 'start'You may want to handle errors from deferred functions.
you can do this with a closure, but you'll need to return an array of errors.func DoThing() (result string, errs []error) { // <-- errs !MUST! be declared here for deferred to be caught defer func() { err = pipe.Close() if err != nil { errs = append(errs, err) } }() return "success", errs }
Function Overloading
Not supported in go.
Anonymous Functions
You can define anynymous functions in go
foo = func() { ... } defer func() { ... } go func() { ... }You can also use the
func
generic type to type these anonymous functions.var sum func(int, int) int sum = func(a, b, int) int { return a + b }
Recursion / Tail Call Optimization
TODO:
Why wouldn't tailCount also continuously increase the stack? It feels like this would be even larger than regular recursion..
Go does not do tail call optimization.
You can force this optimization by executing your recursive function call in a second function.
See https://medium.com/@meeusdylan/tail-recursion-in-go-fb5cf69a0f26func tailCount(i int) { return count(i) } func count(i int) { if i == 20 { return i } return tailCount(i) }