Docs
The docs command pull comments from packages and contribute to the help documentation utility. Below are some examples of the Json.
Documentation
reserved words
These following words are reserced and cannot be used as functions, vars, or other things:
- break
- default
- func
- interface
- select
- case
- defer
- go
- map
- struct
- chan
- else
- goto
- package
- switch
- const
- fallthrough
- if
- range
- type
- continue
- for
- import
- return
- var
predefined identifiers
All things that are given a name by the user is an identifier. Variables, functions, templates, a class, etc. These are case sensitive and must begin with a letter of the UTF-8 family.
As such there are also predefined identifiers:
- append
- bool
- byte
- cap
- close
- complex
- complex64
- complex128
- copy
- false
- float32
- float64
- imag
- int
- int8
- int16
- int32
- int64
- iota
- len
- make
- new
- nil
- panic
- println
- real
- recover
- string
- true
- uint
- uint8
- uint16
- uint32
- uint64
- uintptr
blank identifer
The _ is a blank identifer and when used, its value gets automatically discarded.
naming things
Best pattern for naming variables or functions, or really just any user defined indentifiers is to use these rules:
- methods or functions which is named as a noun
- to change an object, use “SetName”
- MixedCaps or mixedCaps
- no underscores
- no long names
- no indication of the package, but just name) located in the main package
about the method of the object
Get documents for the method of Decode on the Deocoder object
➜ go doc json.Decoder.Decode
package json // import "encoding/json"
func (dec *Decoder) Decode(v interface{}) error
Decode reads the next JSON-encoded value from its input and stores it in the
value pointed to by v.
See the documentation for Unmarshal for details about the conversion of JSON
into a Go value.
about the object
Get documents about the json.Decoder object
➜ go doc json.Decoder
package json // import "encoding/json"
type Decoder struct {
// Has unexported fields.
}
A Decoder reads and decodes JSON values from an input stream.
func NewDecoder(r io.Reader) *Decoder
func (dec *Decoder) Buffered() io.Reader
func (dec *Decoder) Decode(v interface{}) error
func (dec *Decoder) DisallowUnknownFields()
func (dec *Decoder) InputOffset() int64
func (dec *Decoder) More() bool
func (dec *Decoder) Token() (Token, error)
func (dec *Decoder) UseNumber()
about the package
➜ go doc json
package json // import "encoding/json"
Package json implements encoding and decoding of JSON as defined in RFC
7159. The mapping between JSON and Go values is described in the
documentation for the Marshal and Unmarshal functions.
See "JSON and Go" for an introduction to this package:
https://golang.org/doc/articles/json_and_go.html
func Compact(dst *bytes.Buffer, src []byte) error
func HTMLEscape(dst *bytes.Buffer, src []byte)
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
func Unmarshal(data []byte, v interface{}) error
func Valid(data []byte) bool
type Decoder struct{ ... }
func NewDecoder(r io.Reader) *Decoder
type Delim rune
type Encoder struct{ ... }
func NewEncoder(w io.Writer) *Encoder
type InvalidUTF8Error struct{ ... }
type InvalidUnmarshalError struct{ ... }
type Marshaler interface{ ... }
type MarshalerError struct{ ... }
type Number string
type RawMessage []byte
type SyntaxError struct{ ... }
type Token interface{}
type UnmarshalFieldError struct{ ... }
type UnmarshalTypeError struct{ ... }
type Unmarshaler interface{ ... }
type UnsupportedTypeError struct{ ... }
type UnsupportedValueError struct{ ... }
Modules
To make a new module
go mod init github.com/pluralsight/webservice
This creates a file called “go.mod” which specifics the location of the module and the required version of Go to be used.
Now with the module loaded, and a main.go file provided, you can do the following
go run github.com/pluralsight/webservice
This also sets us up for running a go program in more standard way.
Declaration and Primitives
Pointers
func main() {
// declaring a variable
var i int
i = 42
fmt.Println(i)
// another way to declare
var f float32 = 3.14
fmt.Println(f)
// even another way
// := uses implicit type
firstName := "Arthur"
fmt.Println(firstName)
// this is useful for complex mathematics simulations
// allowing for doing complex math
c := complex(3, 4)
fmt.Println(c)
// if you want to split them appart
// you can pull out the real and imaginary
r, im := real(c), imag(c)
fmt.Println(r, im)
}
When declaring variables, its a pointer to a value. Pointers are variables that hold a location of data in memory in another location.
create a variable with empty string
This calls a var with no variable. The *string
is setting the var as a “pointer” by using the *
in front of string
.
the output should give “
attempt #1
func main() {
var firstName *string
fmt.Println(firstName)
firstName = "Arthur"
}
output #1
If you try to set its value, you will get an error because you are trying to set a string to a pointer.
"cannot use "Arthur" (type string) as type *string is assignment"
attempt #2
You could try to dereference the data by reaching through the pointer, grabbing the data, and getting it back, but this will results in an error.
func main() {
var firstName *string
fmt.Println(firstName)
*firstName = "Arthur"
}
output #2
Reason for the error is that we are trying to assign “Arthur” to an uninitialized pointer.
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x56014e8c51ba]
goroutine 1 [running]:
main.main()
/home/addlema/src/golang/main.go:18 +0x7a
exit status 2
attempt #3 - solution #1
func main() {
// declare the var being a pointer operator and a type
var firstName *string = new(string)
// set the var with a string
// using * to reference to the pointer
*firstName = "Author"
// you can print the reference location
fmt.Println(firstName)
// or you can print the data that the pointer
// is referencing, by dereferincing
fmt.Println(*firstName)
}
output #3
attempt #4 - solution #2
func main() {
// solution 2...
firstNameStr := "Arthur"
fmt.Println(firstNameStr)
// create a pointer that is pointing to a variable
ptr := &firstNameStr
// print the location and the dereferenced data
fmt.Println(ptr, *ptr)
// if you change the name...
firstNameStr = "Tricia"
// and print again, you should see the
// location of data should remain the same
// and the dereferenced data should now be "Tricia"
fmt.Println(ptr, *ptr)
}
output #4
Conclusions
- You can use pointers, but declare them first with a type
- If you update the data that the pointer is referencing, the pointer address will not change. Only the data will change.
Go does not support pointer arithmetic
Constants
Difference between constants and variables is that constants cannot change their value over time.
how to declare
func main() {
const pi = 3.1415
}
if you assign.. you get an error
func main() {
const pi = 3.1415
pi = 1.2
}
cannot assign to pi
implicit type constant
Following example show how a constant will interpolate the type based on its data.
func main() {
const c = 3
fmt.Println(c + 3)
// ...
fmt.Println(c + 1.2)
}
This could be constrained by declaring the constant as a type of integer.
func main() {
// note here the addition of "int"
const c int = 3
fmt.Println(c + 3)
// ...
fmt.Println(c + 1.2)
}
Error says you cannot add integer to a floating point.
constant 1.2 truncated to integer
It is possible by converting the constant to a floating 32 point
func main() {
const c int = 3
fmt.Println(c + 3)
// ...
// note here the addition of "float32(c)"
fmt.Println(float32(c) + 1.2)
}
6
4.2
rules…
- When you declare them, you must initialize them at the time of declaring them. It has to be on the same line. It also has to be determined at the compile time. Not at run time.
Conclusions
- Declare and initiaze them on same line
- Determined at compile time. Not run time.
- Its possible convert constants to different data types.
Iota and Constant Expressions
To work with Iota and constant expressions, the constants must exist at the package level. Note how they are outside of the func main()
const (
first = 1
second = "second"
)
func main() {
fmt.Println(first, second)
}
1 second
Now its possible to replace them with the iota
constant.
Note the output of 0 1
. This is because every time iota
is used, its value is incremented by a 1.
const (
first = iota
second = iota
)
func main() {
fmt.Println(first, second)
}
0 1
Another example is the following…
const (
first = iota
second = iota
)
func main() {
fmt.Println(first, second)
fmt.Println(first, second, first)
}
0 1
0 1 0
An example of a constant expression
// this increments by 1 everytime the variable of "second" is used
second = 2 << iota
Another example…
const (
first = iota + 6
second = 2 << iota
)
funct main() {
fmt.Println(first, second)
}
6 4
You also dont have to call iota every time. Below there is an example that reuses the previous iota.
const (
first = iota + 6
second
)
func main() {
fmt.Println(first, second
}
6 7
Its possible to group constants. This also resets the “iota” count as well…
const (
first = iota
second = iota
)
const (
third = iota
)
func main() {
fmt.Println(first, second, third)
}
0 1 0
Conclusions
Iotas help transform constants.
Remember, constants must be evaluated at compile time.
Summary
- Can declare variables being implicite
- Pointers have lots of power
- Operator: addressof
- Constants
- Iota
- Allows for dynamic values at compile time
- Constant expressions
- Iota
Working with collections
Overview
- Array
- Slices (popular)
- Maps
- Structs (similar to a class)
Arrays
Creating
package main
import "fmt"
func main() {
// build an array with 3 elements
// each element is an integer
var arr [3]int
// add some elements to the array
arr[0] = 1
arr[1] = 2
arr[2] = 3
// print the array
fmt.Println(arr)
// print the second element
fmt.Println(arr[1])
}
[1 2 3]
2
Another way to use arrays (shorter)
package main
import "fmt"
func main() {
// build an array with [3] elements
// each element is an integer
// add some elements to the array with {1,2,3}
arr := [3]int{1, 2, 3}
// print the array
fmt.Println(arr)
// print the second element
fmt.Println(arr[1])
}
[1 2 3]
2
Slices
They are built on top of arrays. The slice can be thought of as a pointer. Good for working with an API and select what you want.
method 1
package main
import "fmt"
func main() {
// build an array with 3 elements
// each element is an integer
// add some elements to the array
arr := [3]int{1, 2, 3}
// create a slice
// the : says use start of array to the end of array
slice := arr[:]
fmt.Println(arr, slice)
}
[1 2 3] [1 2 3]
method 2
Below the slice and the array are keeping to the same item in memory
package main
import "fmt"
func main() {
// build an array with 3 elements
// each element is an integer
// add some elements to the array
arr := [3]int{1, 2, 3}
// create a slice
// the : says use start of array to the end of array
slice := arr[:]
// you can use either one to set values
arr[1] = 42
slice[2] = 27
fmt.Println(arr, slice)
}
[1 42 27] [1 42 27]
method 3
Better way to define slices
Its possible as you keep adding items to slices behind the scenes, go is going to manage the array if the memory address runs out of space, but you dont need to really worry about that
package main
import "fmt"
func main() {
// create an array with no size
// the var of slice points to an array
slice := []int{1, 2, 3}
// print the contents of slice
fmt.Println(slice)
// now you can also append items
slice = append(slice, 4, 42, 27)
// its possible as you keep adding items to slices
// behind the scenes, go is going to manage the arrays
// if the memory address runs out of space, but you dont
// need to really worry about that
}
[1 2 3]
[1 2 3 4 42 27]
getting ranges of elements from an array
package main
import "fmt"
func main() {
// create an array with no size
// the var of slice points to an array
slice := []int{1, 2, 3}
// print the contents of slice
fmt.Println(slice)
// now you can also append items
slice = append(slice, 4, 42, 27)
// print the contents of slice
fmt.Println(slice)
// create an index starting at the second element
// and going to the end
s2 := slice[1:]
// create an index grab the first two elements
// its grabing up to 2 elements, starting at
s3 := slice[:2]
// create an index between the second and third index
s4 := slice[1:2]
fmt.Println(s2, s3, s4)
[1 2 3]
[1 2 3 4 42 27]
[2 3 4 42 27] [1 2] [2]
maps
Keys to values
Need to specify the type of key and the type of value
package main
import "fmt"
func main() {
// create a map of type string
// the int will be the values
m := map[string]int{"foo": 42}
// print the map
fmt.Println(m)
// print the value of the key
fmt.Println(m["foo"])
// change the value
m["foo"] = 27
fmt.Println(m)
// delete an element
delete(m, "foo")
// print the empty map
fmt.Println(m)
}
map[foo:42]
42
map[foo:27]
map[]
structs
Possible to associate any type of data together. Fields are fixed at compile time.
package main
import "fmt"
func main() {
// define a type called user
// that user is a struct
type user struct {
ID int
FirstName string
LastName string
}
// create variable of u
// using the struct of user
var u user
// print the variable
// this will show the zero value for the struct
// ID is 0
// FirstName is blank
// LastName is blank
fmt.Println(u)
}
{0 }
package main
import "fmt"
func main() {
// define a type called user
// that user is a struct
type user struct {
ID int
FirstName string
LastName string
}
// create variable of u
// using the struct of user
var u user
// add some data
u.ID = 1
u.FirstName = "Arthur"
u.LastName = "Dent"
// print out the contents
fmt.Println(u)
}
{1 Arthur Dent}
package main
import "fmt"
func main() {
// define a type called user
// that user is a struct
type user struct {
ID int
FirstName string
LastName string
}
// create variable of u
// using the struct of user
var u user
// add some data
u.ID = 1
u.FirstName = "Arthur"
u.LastName = "Dent"
// print out the contents
fmt.Println(u)
// short form
u2 := user{ID: 1, FirstName: "Arthur", LastName: "Dent"}
fmt.Println(u2)
}
{1 Arthur Dent}
{1 Arthur Dent}
// multiline form
u2 := user{ID: 1,
FirstName: "Arthur",
LastName: "Dent",
}
additions
- Possible to move structs outside of main to be at the package level
Packages
By creating a directory inside your main code location, you are creating a package. To interact with the package, call the package with the import command
package main
import (
"fmt"
"github.com/aaronaddleman/golang/models"
)
func main() {
// create a user
u := models.User{
ID: 2,
FirstName: "Tricia",
LastName: "McMillan",
}
fmt.Println(u)
}
And the file of golang/models.go
is the following
package models
// User type
type User struct {
ID int
FirstName string
LastName string
}
var (
// create a array as pointers
// reason for pointers is so that we can
// change the users instead of copying them
user []*User
// usually a database would provide the id,
// here we are cheating
// defaulting to int type
nextID = 1
)
Functions and Methods
- Functions
- Arguments and Parameters
- Return results
- Methods
- Interfaces
Functions
very simple function
func nameofFunc() {
}
function with params and ret1 is returned as type1
func nameOfFunc(param1 type1, param2 type2, ...) ret1 type1 {
}
funciton with params and returning multiple variables
func nameOfFunc(param1 type1, param2 type2, ...) (ret1 type1, ret2 type2, ...) {
}
invoking functions
Below is invoking the function of startWebServer
package main
import (
"fmt"
)
func main() {
fmt.Println("hello")
// invoke the function
startWebServer()
}
func startWebServer() {
fmt.Println("starting server...")
fmt.Println("server started")
}
hello
starting server...
server started
parameters
Below shows how to define a function with a parameter an the format.
package main
import (
"fmt"
)
func main() {
fmt.Println("hello")
// set a var (eg port)
port := 3000
startWebServer(port)
}
// define the function with a defined parameter
// name_of_param type_of_parameter
func startWebServer(port int) {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
}
hello
starting server...
server started on port 3000
multiple parameters
package main
import (
"fmt"
)
func main() {
fmt.Println("hello")
// set a var (eg port)
port := 3000
startWebServer(port, 2)
}
// define the function with a defined parameter
// name_of_param type_of_parameter
func startWebServer(port int, numberOfRetries int) {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
fmt.Println("Number of retries", numberOfRetries)
}
hello
starting server...
server started on port 3000
Number of retries 2
multiple parameters with shorter form
// define the function with a defined parameter
// name_of_param type_of_parameter
//
// notice only one "init" is being set
// this is a little bit shorter as all parameters are
// of type "int"
func startWebServer(port, numberOfRetries int) {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
fmt.Println("Number of retries", numberOfRetries)
}
Returning data
Define the type of data after the parameters. Below is an example.
this is not idiomatic… just an example… its better to return some more information
package main
import (
"fmt"
)
func main() {
fmt.Println("hello")
// set a var (eg port)
port := 3000
isStarted := startWebServer(port, 2)
fmt.Println(isStarted)
}
// define the function with a defined parameter
// name_of_param type_of_parameter
//
// notice only one "init" is being set
// this is a little bit shorter as all parameters are
// of type "int"
//
// the "bool" is the type of data that will be returned
func startWebServer(port, numberOfRetries int) bool {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
fmt.Println("Number of retries", numberOfRetries)
// return true as the bool
return true
}
hello
starting server...
server started on port 3000
Number of retries 2
true
returning an error from a function
Hear the calling function can decide what to do with the returned value.
package main
import (
"fmt"
)
func main() {
fmt.Println("hello")
// set a var (eg port)
port := 3000
err := startWebServer(port, 2)
fmt.Println(err)
}
// the "error" is the type of data that will be returned
func startWebServer(port, numberOfRetries int) error {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
fmt.Println("Number of retries", numberOfRetries)
// return true as the bool
return nil
}
demo of an error
package main
import (
"errors"
"fmt"
)
func main() {
fmt.Println("hello")
// set a var (eg port)
port := 3000
err := startWebServer(port, 2)
fmt.Println(err)
}
// the "error" is the type of data that will be returned
func startWebServer(port, numberOfRetries int) error {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
// return true as the bool
return errors.New("something went wrong")
}
hello
starting server...
server started on port 3000
something went wrong
returning multiple values of information
package main
import (
"fmt"
)
func main() {
fmt.Println("hello")
// set a var (eg port)
port := 3000
// here we use a list to catch the port and error value
port, err := startWebServer(port, 2)
fmt.Println(port, err)
}
// notice the "int, error" is staying its going to return two types of data
// ... an int,
// ... an error, if it exists
// the "error" is the type of data that will be returned
func startWebServer(port, numberOfRetries int) (int, error) {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
// return the port and for now, nil for the error
return port, nil
}
hello
starting server...
server started on port 3000
3000 <nil>
what if a function returns a value but you dont need it
go provides a write only variable called as _
package main
import (
"fmt"
)
func main() {
fmt.Println("hello")
// set a var (eg port)
port := 3000
// here we use a list to catch the port and error value
_, err := startWebServer(port, 2)
fmt.Println(err)
}
// notice the "int, error" is staying its going to return two types of data
// ... an int,
// ... an error, if it exists
// the "error" is the type of data that will be returned
func startWebServer(port, numberOfRetries int) (int, error) {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
// return the port and for now, nil for the error
return port, nil
}
hello
starting server...
server started on port 3000
<nil>
methods
an example of a method is one of the following
package controllers
import "net/http"
// struct with no fields
// useful for a set of related behaviors to be associated
// this also creates that object oriented behavior
type userController struct{}
// create a function bound to a struct
// with the part of "uc userController"
// this binds uc to the userController struct
//
// the function name is ServerHTTP
// it takes a w http responseWriter
// it takes a r http request
func (uc userController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// usually things in go deal with a slice of bytes...
// to pass a string to the write object, we have to do a type converstion
w.Write([]byte("Hello from the User Controller!"))
}
binding data with constructors
package controllers
import (
"net/http"
"regexp"
)
// struct with no fields
// useful for a set of related behaviors to be associated
// this also creates that object oriented behavior
type userController struct {
// handle resource collection and resource users
// match to incomming http requests path
userIDPattern *regexp.Regexp
}
// create a function bound to a struct
// with the part of "uc userController"
// this binds uc to the userController struct
//
// the function name is ServerHTTP
// it takes a w http responseWriter
// it takes a r http request
func (uc userController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// usually things in go deal with a slice of bytes...
// to pass a string to the write object, we have to do a type converstion
w.Write([]byte("Hello from the User Controller!"))
}
// convention used to create a constructor for the UserController
// this returns a pointer to the controller object
// this avoids an unneccesary object copy
func newUserController() *userController {
// take the immediate address of the operator with "&"
// yes, there is no named variable.. this is allowed with struct datatypes
// with int variables... thats not allowed, you have to create a variable
// also this is a local variable, go will recognize that this var needs to be promoted to a higher level
return &userController{
userIDPattern: regexp.MustCompile(`^/users/(\d+)/?`),
}
}
interfaces
These are very powerful. More than other languages.
The handler type offers an interface.
http - The Go Programming Language
Because the following code is written with the parameters of w http.ResponseWriter, r * http.Request
it automatically implements the handler interface of the reference above.
func (uc userController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// usually things in go deal with a slice of bytes...
// to pass a string to the write object, we have to do a type converstion
w.Write([]byte("Hello from the User Controller!"))
}
Here is an example of how it can be used…
package controllers
import "net/http"
// RegisterControllers to handle rough routing
// when network requests are received, it will go to the correct process
//
func RegisterControllers() {
// create a new user controller
uc := newUserController()
// reach into the http function and use the Handle
//
// any object that has the Handle interface, it will implement it
//
// this will match requests of /users
http.Handle("/users", *uc)
// this will match requests of /users/*
http.Handle("/users/", *uc)
}
implementing the http server
main.go
// main.go
package main
import (
"fmt"
"net/http"
"github.com/aaronaddleman/golang-webservice/controllers"
)
func main() {
// reach into the "controllers" package and register the controllers
controllers.RegisterControllers()
// use http package and call "ListenAndServer"
// takes two params;
// ports
// server multiplexer (object that handles all the objects comming in and routing)
//
// using nil for the multiplexer to use the default one
http.ListenAndServe(":3000", nil)
}
// notice the "int, error" is staying its going to return two types of data
// ... an int,
// ... an error, if it exists
// the "error" is the type of data that will be returned
func startWebServer(port, numberOfRetries int) (int, error) {
fmt.Println("starting server...")
fmt.Println("server started on port", port)
// return the port and for now, nil for the error
return port, nil
}
// controllers/user.go
package controllers
import (
"net/http"
"regexp"
)
// struct with no fields
// useful for a set of related behaviors to be associated
// this also creates that object oriented behavior
type userController struct {
// handle resource collection and resource users
// match to incomming http requests path
userIDPattern *regexp.Regexp
}
// create a function bound to a struct
// with the part of "uc userController"
// this binds uc to the userController struct
//
// the function name is ServerHTTP
// it takes a w http responseWriter
// it takes a r http request
func (uc userController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// usually things in go deal with a slice of bytes...
// to pass a string to the write object, we have to do a type converstion
w.Write([]byte("Hello from the User Controller!"))
}
// convention used to create a constructor for the UserController
// this returns a pointer to the controller object
// this avoids an unneccesary object copy
func newUserController() *userController {
// take the immediate address of the operator with "&"
// yes, there is no named variable.. this is allowed with struct datatypes
// with int variables... thats not allowed, you have to create a variable
// also this is a local variable, go will recognize that this var needs to be promoted to a higher level
return &userController{
userIDPattern: regexp.MustCompile(`^/users/(\d+)/?`),
}
}
// controllers/front.go
package controllers
import "net/http"
// RegisterControllers to handle rough routing
// when network requests are received, it will go to the correct process
//
func RegisterControllers() {
// create a new user controller
uc := newUserController()
// reach into the http function and use the Handle
//
// any object that has the Handle interface, it will implement it
//
// this will match requests of /users
http.Handle("/users", *uc)
// this will match requests of /users/*
http.Handle("/users/", *uc)
}
// models/user.go
package models
// User type
type User struct {
ID int
FirstName string
LastName string
}
var (
// create a array as pointers
// reason for pointers is so that we can
// change the users instead of copying them
user []*User
// usually a database would provide the id,
// here we are cheating
// defaulting to int type
nextID = 1
)
// GetUsers for listing the users
func GetUsers() []*User {
return users
}
// AddUser for adding a new user
func AddUser(u User) (User, error) {
u.ID = nextID
// increment the id
nextID++
// append it to the users object
// take the address provided in and store that
users = append(users, &u)
// return the u object and nil for the error
return u, nil
}
Controlling Program Flow
Looping
All loops are using the for
loop
Loop till condition
package main
func main() {
var i int
for i < 5 {
// built in print function
println(i)
i++
}
}
0
1
2
3
4
package main
func main() {
var i int
for i < 5 {
// built in print function
println(i)
i++
if i == 3 {
// breaks out of the loop
break
}
}
}
package main
func main() {
var i int
for i < 5 {
// built in print function
println(i)
i++
if i == 3 {
// continue on with rest of program
continue
}
println("continuing..")
}
}
0
continuing..
1
continuing..
2
3
continuing..
4
continuing..
Loop til condition with post clause
package main
func main() {
// loop til condition with post clause
//
// it requires 3 terms in the for loop
// that will tell the compiler we are
// using the loop til condition with post
// clause
// method 1
var i int
for ; i < 5; i++ {
println(i)
}
println(i)
// method 2
// j is only defined inside the for loop
for j := 0; j < 5; j++ {
println(i)
}
}
Infinite Loops
package main
func main() {
var i int
for ; ; {
if i == 5 {
break
}
println(i)
i++
}
}
package main
func main() {
var i int
for {
if i == 5 {
break
}
println(i)
i++
}
}
Loop over collections
package main
func main() {
slice := []int{1, 2, 3}
//ugly
for i := 0; i < len(slice); i++ {
println(slice[i])
}
//clean
// define an indexer and value
//
// the range tells the compiler the index, and the value at that index
for i, v := range slice{
println(i, v)
}
//clean works with maps
wellKnownPorts := map[string]int{"http": 80, "https": 443}
for k, v := range wellKnownPorts {
println(k, v)
}
}
1
2
3
0 1
1 2
2 3
http 80
https 443
package main
func main() {
slice := []int{1, 2, 3}
//ugly
for i := 0; i < len(slice); i++ {
println(slice[i])
}
//clean
// define an indexer and value
//
// the range tells the compiler the index, and the value at that index
for i, v := range slice{
println(i, v)
}
//clean works with maps
wellKnownPorts_a := map[string]int{"http": 80, "https": 443}
for k, v := range wellKnownPorts_a {
println(k, v)
}
//clean works with maps
// with write-only variable
wellKnownPorts_b := map[string]int{"http": 80, "https": 443}
for _, v := range wellKnownPorts_b {
println(v)
}
}
1
2
3
0 1
1 2
2 3
http 80
https 443
80
443
Branching
Panics
Usually error values are helpful. Eventually, you will find a state where you need to state the app cannot run function.
panic("Something bad happend")
It is possible to recover from panics.
If Statements
package main
type User struct {
ID int
FirstName string
LastName string
}
func main() {
u1 := User{
ID: 1,
FirstName: "Arthur",
LastName: "Dent",
}
u2 := User{
ID: 2,
FirstName: "Arthur",
LastName: "Prefect",
}
if u1.ID == u2.ID {
println("Same user!")
}
if u1.ID != u2.ID {
println("Not Same user!")
}
if u1 == u2 {
println("Same user!")
} else if u1.FirstName == u2.FirstName {
println("Similer user.")
}
}
Switches
package main
type HTTPRequest struct {
Method string
}
func main() {
r := HTTPRequest{ Method: "HEAD" }
switch r.Method {
case "GET":
println("Get Request")
fallthrough
case "DELETE":
println("Delete request")
default:
println("Unhandled method")
}
}