Learning golang once and for all
Golang Learning
Json unmarshaling
Structs and json tags
Something i always forget is that not only the `json:“field”` thing is not necessary but the key names have to be uppercased:
type User struct {
Id int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Username string `json:"username"`
Address struct {
Street string `json:"street"`
City string `json:"city"`
} `json:"address"`
}
Otherwise things don’t un marshal properly , also nice you can nest structs like that
Main idea about having a data struct defined before unmarshaling
I struggled to come to terms with this idea , but if you are consuming a large json object from any give endpoint you are likely to know the format or what values are you after. The values that are not declared in the type you are unmarshaling into ,simple don’t unmarshal
for example if you have this json:
{
"name": "Jerry",
"age": : 40,
"email": "garciaj.uk@gmail.com"
}
and you unrmarshal into:
type User struct {
Name string `json:"name"`
}
That will only unmarshal the name and discard everything else … in a way kind of a nice thing.
Channels Select and GoRoutines
Basics
So you know channels are a way for goroutines to receive and send data and they have a type eg , This is declaring a channel of type int and after we declare an anonymous function that we tell it should take a chan int as an argv and at the end of the function we pass out the channel itself
out := make(chan int)
go func(c chan int) {
defer close(c)
c <- 1
time.Sleep(1 * time.Second)
c <- 2
time.Sleep(1 * time.Second)
c <- 3
time.Sleep(1 * time.Second)
c <- 4
time.Sleep(1 * time.Second)
}(out)
You can see how doing simply
c <- 1
is enough to send data into the channel , now if you want to get data out of the channel is somewhat similar
fmt.Println(<-c)
or something like
mymsg := <-c
Also channels return two arguments when reading from them , the second argument is a bool that says if the channel is still open , and therefor you should still be reading from it:
msg, open := <-out:
open is the bool that tells you if the channel is still open
What happens if you have multiple channels?
The more goroutines you might run the more channels you need to create, there’s a function called select which can take a bunch of channels and read from whatever channel is ready first:
select {
case msg, open := <-out:
fmt.Printf("Message from out: %d\n", msg)
case msg1, open1 := <-out1:
fmt.Printf("Message from out1: %d\n", msg1)
}
The good thing about select , is that it actually reads from whatever channel is ready first , is not sequential or blocking.
Scoped variables
declaring variable inside ifs or others
package main
import "fmt"
func main() {
a := 1
fmt.Println(a)
{
a := 2
fmt.Println(a)
}
fmt.Println(a)
}
prints 1 2 1
you need to
func main() {
a := 1
fmt.Println(a)
{
a = 2
fmt.Println(a)
}
fmt.Println(a)
}
and that should print 1 2 2
Pass by value pass by reference
This would explain everything you need to know about pointers
Pass by value, you just passing the value and not the memory location , so in short it copies the value into a function Negatives of that depend on how large the value is
Pass by reference , passes a pointer to the real variable so when you change it inside a function it does change the value of the real variable
fmt.Println("Pass by value")
myint := 10
fmt.Println(myint)
// pass by value ->
func(m int) {
m++
fmt.Println(m)
}(myint)
fmt.Println(myint)
/*This prints
10
11
10
*/
// pass by referece ->
fmt.Println(myint)
func(m *int) {
*m = 100
fmt.Println(*m)
}(&myint)
fmt.Println(myint)
/*This prints
10
100
100
*/
Pointers
It’s important to note also that the json unmarshal function takes pointers to the variable you just declared (see &posts)
var posts []User
...
body, err := ioutil.ReadAll(resp.Body)
...
json.Unmarshal(body, &posts)
for u := range posts {
fmt.Printf("User: %s\n", posts[u].Name)
fmt.Printf("Address.Street: %s\n", posts[u].Address.Street)
fmt.Printf("Address.City: %s\n", posts[u].Address.City)
}
Buffers and Readers
IO Readers
func main() {
conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Fprint(conn, "GET HTTP 1.0\r\n\r\n")
buf := make([]byte, 20)
if err != nil {
panic(err)
}
for {
n, err := conn.Read(buf)
if err == io.EOF {
break
}
if n > 0 {
fmt.Println(string(buf[:n]))
}
}
}
Custom IO Writer
So an interface is whatever struct that implements a given method , i don’t really get this 100% but
If there’s a struct called dog that has a function called Walk , and there is an interface that says something like :
type Animals interface {
Walk(b []byte) (n int, err error)
}
Then the struct called Dog implements the interface called Animal , maybe it is like classes but quite implicit, as you don’t have to write: Dog implements Animal kind of thing like in java
So i declare an interface called my MyWriter , that has a signature method (function) called Write, Then i define a struct that without knowing will be part of the interface , because it implements a Write function
And because of that i can print to it.
the go definition of fmt.Fprint is:
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
So it needs an io.Writer , io.Writer look like:
type Writer interface {
Write(p []byte) (n int, err error)
}
So what i get is that as long pass a struct to fmt.Fprint that implements ANY interface that has a Write function , then its ok
So this is my code:
type MyWriter interface {
Write(b []byte) (n int, err error)
}
type MyRealWriter struct {
}
func (rr MyRealWriter) Write(b []byte)(n int , err error) {
fmt.Printf("%T -> %d\n",b , len(b))
fmt.Println(string(b))
return 10,nil
}
func main() {
var ddd MyRealWriter
fmt.Fprint(ddd, "jeronimo")
}
And it works!
Go Templates
A very tiny example
So templating is good to generate files and placeholders, for example
type Me struct {
Name string
Lastname string
}
func main() {
td := Me{"Jeronimo","Garcia"}
t, err := template.New("todos").Parse("Hi {{ if eq .Name \"Jeronimo\" }} {{ .Lastname }} {{ else }} Anonymous {{ end }} \n")
if err != nil {
panic(err)
}
err = t.Execute(os.Stdout, td)
if err != nil {
panic(err)
}
}
You can see Template.New takes a random name todos , but the Parse method takes a string where the values will be interpolated
Executing the templates
Now on the Execute step we pass where we want this to be rendered os.Stdout and the struct we want to use to interpolate with:
err = t.Execute(os.Stdout, td)
if err != nil {
panic(err)
}
FuncMap
You can make your own functions and map them into the template engine so they can be used inside the template eg:
t, err := template.New("todos").Funcs(template.FuncMap{"isitme":func(person string) bool {
if person == "Jeronimo" {
return true
}
return false
}}).Parse("Hi {{ if eq .Name \"Jeronimo\" }} {{ .Lastname }} {{ else }} Anonymous {{ end }} {{ if isitme .Name }} It's ME! {{else}} not me!{{end}}\n")
pretty simple , the output is
jgarcia at x1 in ~/Projects/si/golang/templates
$ go run main.go
Hi Garcia It's ME!
Mux and net/http and socket
Writing binary data in sockets
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:5672")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
b := make([]byte,8)
binary.LittleEndian.PutUint16(b[0:], 0x0008)
binary.LittleEndian.PutUint16(b[1:6], 0x0000)
binary.LittleEndian.PutUint16(b[6:], 0xce00)
binary.Write(conn,binary.LittleEndian,b)
if err != nil {
panic(err)
}
}
Packages
Interfaces
How to check if a type implements an interface
type Cars interface {
Run(wheels int) (runs bool)
}
type Twingo struct {
}
type Xr struct {
}
func (t Twingo) Run(w int) (runs bool) {
if w == 4 {
return true
}
return false
}
func main() {
var t Twingo
fmt.Println(t.Run(4))
// This is to check if a type implements an interface
var _ Cars= (*Twingo)(nil)
var _ Cars= (*Xr)(nil)
}
Which returns
$ go run main.go
# command-line-arguments
./main.go:27:6: cannot use (*Xr)(nil) (type *Xr) as type Cars in assignment:
*Xr does not implement Cars (missing Run method)
Casting variables and types
So this is mainly to cast empty interfaces to types for example
func main() {
var m interface{} = 1
mm := m.(int)
fmt.Println(strconv.Itoa(mm))
}
So you basically assign any value to an empty interface and then you cast it to .(string) for example
This is a more complicated example where i try to move from “{ “name”: [1,2,3] } to a proper data type:
func main() {
var caca interface{}
myjson := `{"name": [11,22,33] }`
json.Unmarshal([]byte(myjson), &caca)
mcaca, ok := caca.(map[string]interface{})
if !ok {
panic(ok)
}
cc := mcaca["name"]
fmt.Println(reflect.TypeOf(cc))
for i,d := range cc.([]interface{}){
fmt.Println(i)
fmt.Println(reflect.TypeOf(d))
}
}
the tricky bit is actually casting the “[11,22,33]” as a “[]interface{}” , as the key part happens on:
for i,d := range cc.([]interface{}){
So the key is already casted as a string
Generics
Is a way to pass generic types to a function , as long as the fulfill the interface ,
For example without generics if you wanted to implement a Join() function you would need to define one for int one for stirngs and so fort.
Generics kind of fix this
type Auto interface {
Run() bool
}
type Car struct {
}
func (c Car) Run() bool{
return true
}
func Runs[T Auto](things []T) (result []bool) {
for _, v := range things {
result = append(result,v.Run())
}
return result
}
func main() {
var xr Car
var twingo Car
fmt.Print(Runs([]Car{twingo,xr}))
}