DMarlon Studio

Lesson 20 · 20 of 20

Project: a shop inventory · Go

The final project of the basic Go course: a shop inventory built with structs, methods, errors, slices, loops and Printf.

Go · Project: a shop inventory in DMarlon Studio: the example's code and its output in the terminal

This program brings together what the course has covered. Product is a struct with a name, a price and a stock, and shop is a slice of products written as a literal, without repeating the type for each element.

Value is a method with a value receiver: it only reads, and returns the price times the stock. Sell has to change the stock, so its receiver is a pointer. If more units are asked for than there are, it changes nothing and returns an error built with fmt.Errorf; otherwise it subtracts them and returns nil.

In main, sales says how many units of each product are sold, in the same order, and total starts at 0.0. for i := range shop walks the indexes: with shop[i].Sell(...) the method works on the product inside the slice, not on a copy. The error is checked with if err := ...; err != nil: the teapot has only 3 units and 4 are asked for.

Then p := shop[i] takes the updated product and Printf prints it in columns with %-9s, %6.2f, %5d and %9.2f, while total adds up its value. What is left in the stockroom is worth 239.40 euros.

In DMarlon Studio, logic like this can live in the Go server of the Web (Go) target, with the interface in the browser. And the advanced Go course picks up right where this one ends.

Code · main.go

package mainimport "fmt"type Product struct {	Name  string	Price float64	Stock int}func (p Product) Value() float64 { return p.Price * float64(p.Stock) }// Sell lowers the stock, or returns an error if there is not enough.func (p *Product) Sell(units int) error {	if units > p.Stock {		return fmt.Errorf("%s: asked for %d, only %d left", p.Name, units, p.Stock)	}	p.Stock -= units	return nil}func main() {	shop := []Product{{"Notebook", 2.5, 40}, {"Pen", 1.2, 100}, {"Mug", 6.95, 12}, {"Teapot", 18, 3}}	sales, total := []int{5, 30, 10, 4}, 0.0 // units sold of each product	for i := range shop {		if err := shop[i].Sell(sales[i]); err != nil {			fmt.Println("Error:", err)		}		p := shop[i]		fmt.Printf("%-9s%6.2f%5d%9.2f\n", p.Name, p.Price, p.Stock, p.Value())		total += p.Value()	}	fmt.Printf("Stock value: %.2f\n", total)}

Output · go run main.go

Notebook   2.50   35    87.50
Pen        1.20   70    84.00
Mug        6.95    2    13.90
Error: Teapot: asked for 4, only 3 left
Teapot    18.00    3    54.00
Stock value: 239.40

Key points

  • A struct describes the data; its methods, what you do with it.
  • Value receiver to read, pointer receiver to change.
  • Functions that can fail return an error.
  • shop[i].Method() works on the slice element, not on a copy.
  • Printf with widths turns the data into a table.

Exercise

Add a Category field and a map[string]float64 that adds up the value per category; print it at the end in alphabetical order with sort.Strings.