// sample-program.go: a small, complete Go program with numbered comments. // Free sample from 99helpers.com/file-types/go // // It prints a stock report for a few fictional shop items, sorted by value. // // Run it: go run sample-program.go // Build it: go build sample-program.go (makes sample-program, or // sample-program.exe on Windows: one file, no runtime to install) // 1. The package clause comes first in every Go file. Package main plus a // func main makes a program; any other name makes a library package. package main // 2. Imports, from the standard library here. Importing a package and not // using it is a compile error, not a warning. import ( "cmp" "errors" "fmt" "slices" "sync" ) // 3. A constant, declared at package level. const lowStock = 5 // 4. A struct type. Names starting with a lower-case letter are private to // the package; an upper-case first letter exports them. type item struct { name string stock int price float64 } // 5. A method: value belongs to the item type, and it receives a copy of it. func (it item) value() float64 { return float64(it.stock) * it.price } // 6. Interfaces are satisfied implicitly. Having a String method makes item // a fmt.Stringer, so fmt prints items with it automatically. func (it item) String() string { return fmt.Sprintf("%s (%d in stock)", it.name, it.stock) } // 7. Errors are ordinary values. A function that can fail returns an error // as its last result, and the caller checks it. var errNotFound = errors.New("item not found") func find(items []item, name string) (item, error) { for _, it := range items { if it.name == name { return it, nil } } return item{}, fmt.Errorf("find %q: %w", name, errNotFound) } func main() { // 8. A slice (a growable list) built with a composite literal. items := []item{ {"Notebook", 12, 3.50}, {"Pencil", 40, 0.80}, {"Stapler", 2, 7.25}, {"Ruler", 0, 1.20}, {"Glue stick", 5, 1.95}, } // 9. Sorting with a function literal (a closure): largest value first. slices.SortFunc(items, func(a, b item) int { return cmp.Compare(b.value(), a.value()) }) fmt.Printf("%-12s %5s %7s %8s\n", "Item", "Stock", "Price", "Value") for _, it := range items { flag := "" if it.stock < lowStock { flag = " low" } fmt.Printf("%-12s %5d %7.2f %8.2f%s\n", it.name, it.stock, it.price, it.value(), flag) } // 10. Goroutines and a WaitGroup: each item's value is computed // concurrently, and a mutex guards the shared total. var ( wg sync.WaitGroup mu sync.Mutex total float64 ) for _, it := range items { wg.Add(1) go func(it item) { defer wg.Done() // 11. defer runs when the function returns v := it.value() mu.Lock() total += v mu.Unlock() }(it) } wg.Wait() fmt.Printf("Total stock value: %.2f\n", total) // 12. Checking errors: errors.Is looks through the wrapped error. if it, err := find(items, "Pencil"); err == nil { fmt.Println("Found:", it) } if _, err := find(items, "Eraser"); errors.Is(err, errNotFound) { fmt.Println("Error:", err) } }