 
          Development & AI | Alper Akgun
In this followup blog, I will learn a bit more about go. I study through the examples at use https://gobyexample.com
Go supports pointers.
package main
import "fmt"
func val42(ival int) {
    ival = 42
}
func ptr42(iptr *int) {
    *iptr = 42
}
func main() {
    i := 1
    fmt.Println("start:", i)
    val42(i)
    fmt.Println("Zero val:", i)
    ptr42(&i)
    fmt.Println("Zero ptr:", i)
    fmt.Println("Pointer:", &i)
}
            Structs are collection of typed fields. They enabled object oriented programming.
package main
import "fmt"
type student struct {
    name string
    age  int
    grade int
}
func newStudent(name string) *student {
    p := student{name: name}
    p.age = 42
    p.grade = 95
    return &p
}
func main() {
    fmt.Println(student{"Bob", 12})
    fmt.Println(student{name: "Alice", age: 32, grade: 100})
    fmt.Println(&student{name: "Ann", age: 42})
    fmt.Println(newStudent("John"))
    s := student{name: "Steve", age: 5, grade: 90}
    fmt.Println(s.name)
    sp := &s
    fmt.Println(sp.age)
    sp.age = 51
    fmt.Println(sp.age)
    circle := struct {
        name   string
        isFilled bool
    }{
        "MyCircle",
        true,
    }
    fmt.Println(circle)
}            Methods can be defined on struct types.
package main
import "fmt"
type ellipse struct {
    width, height int
}
func (r *ellipse) area() int {
    return r.width * r.height * 3.14159
}
func main() {
    r := ellipse{width: 10, height: 5}
    fmt.Println("area: ", r.area())
    rp := &r
    fmt.Println("area: ", rp.area())
}
            Interfaces allow methods to be typed and added to structs.
package main
package main
import (
    "fmt"
    "math"
)
type shape interface {
    area() float64
    perim() float64
}
type rectangle struct {
    width, height float64
}
type circle struct {
    radius float64
}
func (r rectangle) area() float64 {
    return r.width * r.height
}
func (r rectangle) perim() float64 {
    return 2*r.width + 2*r.height
}
func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}
func measure(g shape) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perim())
}
func main() {
    r := rectangle{width: 3, height: 4}
    c := circle{radius: 5}
    measure(r)
    measure(c)
}
            Embedding structs and interfaces allow type composition.
package main
import "fmt"
type construction struct {
    num int
}
func (b construction) describe() string {
    return fmt.Sprintf("construction %v", b.num)
}
type house struct {
    construction
    str string
}
func main() {
    co := house{
        construction: construction{
            num: 1,
        },
        str: "some name",
    }
    fmt.Printf("construction={num: %v, str: %v}\n", co.num, co.str)
    fmt.Println("also num:", co.construction.num)
    fmt.Println("describe:", co.describe())
    type describer interface {
        describe() string
    }
    var d describer = co
    fmt.Println("describer:", d.describe())
}            Generics allow making types polymorhic, similar to C++ templates.
package main
import "fmt"
func KeyMapper[K comparable, V any](m map[K]V) []K {
    r := make([]K, 0, len(m))
    for k := range m {
        r = append(r, k)
    }
    return r
}
type List[T any] struct {
    head, tail *element[T]
}
type element[T any] struct {
    next *element[T]
    val  T
}
func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
        lst.head = &element[T]{val: v}
        lst.tail = lst.head
    } else {
        lst.tail.next = &element[T]{val: v}
        lst.tail = lst.tail.next
    }
}
func (lst *List[T]) GetAll() []T {
    var elems []T
    for e := lst.head; e != nil; e = e.next {
        elems = append(elems, e.val)
    }
    return elems
}
func main() {
    var m = map[int]string{1: "2", 3: "9", 4: "16"}
    fmt.Println("keys:", KeyMapper(m))
    _ = KeyMapper[int, string](m)
    lst := List[int]{}
    lst.Push(12)
    lst.Push(29)
    fmt.Println("list:", lst.GetAll())
}            Go has explicitly returned error handling, unlike exception mechanisms in other languages.
package main
import (
    "errors"
    "fmt"
)
func fnot42(arg int) (int, error) {
    if arg == 42 {
        return -1, errors.New("can't work with 42")
    }
    return arg + 3, nil
}
type argError struct {
    arg  int
    prob string
}
func (e *argError) Error() string {
    return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func fplus3(arg int) (int, error) {
    if arg == 42 {
        return -1, &argError{arg, "can't work with it"}
    }
    return arg + 3, nil
}
func main() {
    for _, i := range []int{7, 42} {
        if r, e := fnot42(i); e != nil {
            fmt.Println("fnot42 failed:", e)
        } else {
            fmt.Println("fnot42 worked:", r)
        }
    }
    for _, i := range []int{7, 42} {
        if r, e := fplus3(i); e != nil {
            fmt.Println("fplus3 failed:", e)
        } else {
            fmt.Println("fplus3 worked:", r)
        }
    }
    _, e := fplus3(42)
    if ae, ok := e.(*argError); ok {
        fmt.Println(ae.arg)
        fmt.Println(ae.prob)
    }
}