{"id":560,"date":"2022-04-19T10:26:00","date_gmt":"2022-04-19T10:26:00","guid":{"rendered":"https:\/\/andrejacobs.org\/?p=560"},"modified":"2022-04-22T10:29:05","modified_gmt":"2022-04-22T10:29:05","slug":"learning-go-day-3","status":"publish","type":"post","link":"https:\/\/andrejacobs.org\/study-notes\/learning-go-day-3\/","title":{"rendered":"Learning Go – Day 3"},"content":{"rendered":"\n
Featured image by\u00a0Egon Elbre<\/a><\/p>\n\n\n\n In most other languages, a method is a function that belongs to a struct or class. Methods in Go are \u201csimilar\u201d and is declared by specifying a receiver type between the func keyword and the function name (in this case the method name).<\/p>\n Think of the receiver as \u201cself\u201d or \u201cthis\u201d in other languages.<\/p>\n Example<\/p>\n TODO: I wonder if it is possible to pass a struct by reference and not allow the method to mutate it state. Like a read-only pointer (const * const type of thing).<\/p>\n An interface defines a set of method signatures. When a type implements all of the methods, it is said to implement the interface. This is similar to interfaces and protocols in other languages.<\/p>\n Featured image by\u00a0Egon Elbre […]<\/p>\n\n
\n
\n
Structs<\/h3>\n
\n
type Person struct {\n\tname string\n\tage int\n}\n\np1 := Person{\n\tname: "Jannie",\n\tage: 31, \/\/<-- note the trailing semi colon\n}\n\n\/\/ without specifying field names\np2 := Person{"Koos", 46}\n<\/code><\/pre>\n
\n
p1 := struct {\n\tname string\n\tage int\n}{\n\tname: "Anonymous Person",\n\tage: -42, \/\/<-- note the trailing semi colon\n}\n<\/code><\/pre>\n
\n
fmt.Println(p1.name, p1.age)\n<\/code><\/pre>\n
\n
p4 := Person{name: "Age Zero"}\nfmt.Println(p4) \/\/ {Age Zero 0}\n\n\/\/ Doesn't work when you leave out the field names\np4 := Person{"won't compile"}\n<\/code><\/pre>\n
\n
p5 := &Person{\n\tname: "Pointer to a Person",\n\tage: 4200,\n}\n\nfmt.Printf("Type: %T\\n", p5) \/\/ Type: *main.Person \nfmt.Println("Name:", (*p5).name) \/\/ Name: Pointer to a Person\n\/\/ You also don't have to dereference! Nice.\nfmt.Println("Name:", p5.name)\n<\/code><\/pre>\n
\n
type Person struct { \n string\n int\n}\n\np6 := Person{\n\tstring: "Sannie", \/\/<- Note that the field name is by default also the type name\n int: 23,\n}\nfmt.Println(p6.string)\n<\/code><\/pre>\n
\n
==<\/code> if the fields are comparable.<\/li>\n<\/ul>\n
Methods<\/h3>\n
func (receiver Type) methodName(parameters) { \n}\n<\/code><\/pre>\n
type Person struct {\n\tname string\n\tage int\n}\n\nfunc (p Person) sayHello() {\n\tfmt.Printf("Hello, my name is %s and I am %d years old\\n", p.name, p.age)\n}\n\np1 := Person{\n\tname: "Jannie",\n\tage: 24,\n}\n\np1.sayHello() \/\/ Hello, my name is Jannie and I am 24 years old\n<\/code><\/pre>\n
\n
func (p *Person) mutateMeForPain() {\n}\n<\/code><\/pre>\n
\n
\/\/ Normal function\nfunc area(r rectangle) { \n...\n}\n\n\/\/ Method\nfunc (r rectangle) area() { \n...\n}\n\nr := rectangle{...}\narea(r)\nr.area()\n\np := &r\n\/\/ area(p) <-- won't compile\np.area()\n<\/code><\/pre>\n
\n
type SuperInt int\n\nfunc (a SuperInt) add(b SuperInt) SuperInt { \n return a + b\n}\n\na := 5\na.add(SuperInt(10))\n<\/code><\/pre>\n
Interfaces<\/h3>\n
type Tokenizer interface {\n\tSplitIntoTokens() []string\n}\n\ntype ExtendedString string \/\/<-- need to have an alias so we can add methods to string\n\nfunc (s ExtendedString) SplitIntoTokens() []string {\n...\n}\n\ntokens := someString.SplitIntoTokens()\n<\/code><\/pre>\n
\n
interface{}<\/code>. The empty interface has no method requirements and thus all types implement the empty interface also referenced as
interface{}<\/code><\/li>\n<\/ul>\n
func describe(i interface{}) { \n fmt.Printf("Type = %T, value = %v\\n", i, i)\n}\n\ndescribe("This is a string") \/\/ Type = string, value = This is a string\ndescribe(42) \/\/ Type = int, value = 42\ndescribe(true) \/\/ Type = bool, value = true\n<\/code><\/pre>\n
\n
i.(T)<\/code> to get the underlying type of an interface. For example to write an assert (bad example) function.<\/li>\n<\/ul>\n
func assertInt(i interface{}) { \n\ta := i.(int)\n\tfmt.Println(a)\n}\n\nassertInt(42)\nassertInt("Bomb!") \/\/ panic: interface conversion: interface {} is string, not int\n\n\/\/ A better approach would be to use\nv, ok := i.(int)\n\/\/ ok will be true if the underlying type is int\n\/\/ v will be the zero value of the underlying type\n\/\/ if ok is false (and thus underlying) type is not int\n<\/code><\/pre>\n
\n
i.(type)<\/code>.<\/li>\n<\/ul>\n
switch i.(type) {\ncase string:\n\t...\ncase int:\n\t...\n}\n<\/code><\/pre>\n
\n
type SalaryCalculator interface { \n DisplaySalary()\n}\n\ntype LeaveCalculator interface { \n CalculateLeavesLeft() int\n}\n\ntype EmployeeOperations interface { \n SalaryCalculator\n LeaveCalculator\n}\n\n\/\/ Thus any type that want to conform to EmployeeOperations must implement both\n\/\/ SalaryCalculator and LeaveCalculator\n<\/code><\/pre>\n
\n
nil<\/code>.<\/li>\n<\/ul>\n
Concurrency with Goroutines<\/h3>\n
\n
func hello() { \n fmt.Println("Hello world")\n}\n\ngo hello()\nfmt.Println("This will run concurrently with hello()")\ntime.Sleep(1 * time.Second)\n<\/code><\/pre>\n
\n
go functionCall<\/code> will schedule the goroutine to be run and return immediately (ignoring any return value from the function\/method).<\/li>\n<\/ul>\n
Channels<\/h3>\n
\n
chan T<\/code> to declare a channel of type T. Meaning the channel can send and receive type T.<\/li>\n
nil<\/code>.<\/li>\n
make(chan T)<\/code> to create a new channel.<\/li>\n<\/ul>\n
channel1 := make(chan int)\nhired := make(chan Person)\n<\/code><\/pre>\n
\n
channel1 <- value\n<\/code><\/pre>\n
\n
value := <- channel\n<\/code><\/pre>\n
\n
ch := make(chan int)\nch <- 5\n\/\/ fatal error: all goroutines are asleep - deadlock!\n<\/code><\/pre>\n
\n
\/\/ A channel that can only be used for sending\n\/\/ Not much use!\nch := make(chan<- int)\n\n\/\/ Instead you will use channel conversion\n\/\/ To "constrain" that the method may only read or write on.\nfunc sendOnly(chan<- int) {\n\tchan <- 310\n}\n\nch := make(chan int) \/\/ can read and write\ngo sendOnly(ch)\n<- ch\n<\/code><\/pre>\n
\n
close(channel)<\/code> to close a channel. Normally senders use this to indicate no more values are coming down the pipe.<\/li>\n
v, ok := <-channel<\/code> to check if
ok<\/code> is false to indicate the channel has been closed.<\/li>\n
for range<\/code> can be used to iterate over values until the channel is closed.<\/li>\n<\/ul>\n
for v := range channel {\n...\n}\n<\/code><\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"