Skip to content

Instantly share code, notes, and snippets.

@sagivo
Forked from honkskillet/byte-sizetuts.md
Last active August 29, 2015 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sagivo/a9d42125b8c5221174f7 to your computer and use it in GitHub Desktop.
Save sagivo/a9d42125b8c5221174f7 to your computer and use it in GitHub Desktop.

byte-size tuts

by alexander white ©

golang tutorials

Youtube: link to playlist

golang 0 - installation (on a Mac)

YOUTUBE VIDEO TUTORIAL

If it isn't already on your system, Install homebrew. Occasionally, OS X updates may break homebrew, in which case unistall and reinstall it. From the Terminal, install golang and mercurial.

brew install hg go

Setup the GOPATH. We also add a couple of paths to PATH for convenience.

mkdir $HOME/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
export PATH=$PATH:/usr/local/opt/go/libexec/bin

Now let's make a Hello World application. Make a new directory, cd into that directory, and create a new .go file.

mkdir -p $GOPATH/src/github.com/hello
cd $GOPATH/src/github.com/hello
touch hello.go

With your favorite text editor, open hello.go, copy in the following code, and save the file.

package main
import "fmt"
func main() {
	fmt.Printf("Hello, world.\n")
}

Run your go file

go run hello.go

Alternatively, you can compile the .go file into a binary, and execute the binary.

go install
hello

golang 1 - packages

YOUTUBE VIDEO TUTORIAL

Go application can be organized into multiple files by using packages. Go has a rich standard library of packages built in. To import a package use the import keyword. The following line imports the fmt package.

import "fmt"

You can have as many lines of import statements as you wish, but there is also a shorthand for importing multiple packages.

import(
  "fmt"
  "math/rand"
)

By conventions, the name of the package object that is imported is the last component of the path of the the package name. For import "fmt" that will simply be fmt. For import "math/rand" it will be rand. For example,

fmt.Println("Hello World")
rand.Seed(1413241234)
var randomNumer int = rand.Intn(15)

You can create your own packages. Create the file ~/go/src/life/life.go. Copy in the following code using your favorite text editor.

package life
var Life=42
func Halflife() int{
	return Life/2
}

Capitalized variables and functions that are declared in a package will be "public", and can be called via dot notation. Lower case variables and functions are private. You could use the life package in demo.go as follows.

package main
import(
  "fmt"
  "life"
)
func main(){
  fmt.Println("Life = ", life.Life, " and halflife = ", life.Halflife())
}

This would yield.

go run demo.go
Life = 42 and halflife = 21

golang 2 - remote packages

YOUTUBE VIDEO TUTORIAL

Go makes it easy to copy remote repositories into your Go project using the command line tool go get. Let's copy the go-martini repo into our project.

go get github.com/go-martini/martini

This will add the go-martini into the ~/go/pkg file. Now we should be able to import go-martini into one of our own Go programs.

import "github.com/go-martini/martini"

Martini can be used to make a webserver. A Go program that runs a simple webserver on you local host might look like,

package main
import "github.com/go-martini/martini"
func main() {
  m := martini.Classic()
  m.Get("/", func() string {
    return "Hello world!"
  })
  m.Run()
}

Save this code to the file ~/go/src/server/server.go. Run your server with the command,

go run server.go

If everything went as planned, you should have a server running on http://localhost:3000

golang 3 - variable syntax

YOUTUBE VIDEO TUTORIAL

To declare a variable use the format var [variable-name] [type]. Notice that the type comes last. For example,

var myInteger int

You can also initialize a variable right in the declaration.

var myInteger int = 10

You can declare and initialize multiple variables at once.

var myInt, yourInt int = 10, 20

You can declare a LOT of variables at once.

var(
  x, y, z int = 1, 2, 3
  hiThere string = "hello world"
  befuddling complex64 = 3.14 + 6.28i
)

Within the scope of a function, there is a shorthand for declaring and initialize variables using the := operator. The following two lines are identical.

//these two lines are identical
var x int = 99
x := 99 

These are all the variable types supported in go...

bool
string
int  int8  int16  int32  int64 uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32, represents a Unicode code point
float32 float64

You can also declare a constant.

const forever string = "long time" //a type constant
const tomorrow = "short time"      // an untyped constant

You can read more about typed and untyped constants on the official Go blog

golang 4 - functions syntax

YOUTUBE VIDEO TUTORIAL

Function syntax in Go is C-like, but with some big differences. For one, when declaring a function the type comes last (similar to variable). The format is func [function-name]([parameters]) [return-type].

func halfer(whole int) int{
  return whole/2
}

When declaring multiple parameters of the same type, only the last variable needs to have the type specified.

func halfer(onePart,anotherPart int)int{
  return (onePart+anotherPart)/2
}

A big change from C is that functions can return more than one variable.

func sixtyFourty(whole int) (int,int) {
  sixty := whole *6/10
  fourty := whole-sixty
  return sixty, fourty
}
func main(){
  var onePart, anotherPart int
  onePart, anotherPart = sixtyFourty(99)
}

We can also give the return values names in the fucntion declaration. Rewriting the sixtyFourty() function,

func sixtyFourty(whole int) (sixty, fourty int) {
  sixty = whole *6/10
  fourty = whole-sixty
  return // we don't have to specify the return variable because we have already defined them in the func declaration
}

golang 5 - loops syntax

IMAGE ALT TEXT HERE

for loops are C-like, except you do not put ()s around the parameters. You can also use the := operator to declare the iterator variables. The {}s are mandatory.

for i:=0; i<10; i++{
  fmt.Println(i)
}

You can exclude some or all of the parameters.

counter := 10
for ; counter>0; {
  fmt.Println(counter)
  counter --
}

At this point, our for loop functions identically to a while loop. In fact, there are no while or do-while loops in Go at all. The syntax the for/while loops can be simplified further by ommitting the ;s.

counter := 10
for counter>0 { //a while loop in go
  fmt.Println(counter)
  counter --
}

If we leave out all the parameters, a for loop will become infinite. The break keyword can be used to exite any loop.

for  { //loop forever... 
  if isDone(){
    break //unless you break out of the loop
  }
}

You can also loop over an array, slice, map or string in Go using the range keyword.

mySlice := []string{"a","b","y","z"}
for index, element := range mySlice {
  fmt.Println(index, element)  //will print "0 a", etc... 
}

If you don't care about the index, you can use _ instead of a variable. _ indicates we expect a variable to be returned, but we don't care about it's value. On the other hand, if we dont' care about the element, we simply exclude , element.

iq := map[string]int{"UT": 115, "OU": 90, "A&M": 75}
for index := range iq {
  fmt.Println(index, element)  //will print "UT", etc... 
}
for _, element := range iq {
  fmt.Println( element)  //will print "115", etc... 
}

golang 6 - if/else syntax

YOUTUBE VIDEO TUTORIAL

For if statements in Go you do not place ()s around the condition. However, you must place {}s around the body. Mandatory!

random := rand.Intn(10)
if random<5 {
  fmt.Println("our number is less than 5")
}

An else statment must be on the same line as the preceding }.

random := rand.Intn(10)
if random<5 {
  fmt.Println("our number is < 5")
} else {
  fmt.Println("our number is >= 5")
}

The form of theelse if follows accordingly.

random := rand.Intn(10)
if random<3 {
  fmt.Println("our number is < 3")
} else if random<7 {
  fmt.Println("our number is <7")
} else {
  fmt.Println("our number is >= 7")
}

Similar to a for statements, we can also declare and intialize a variable right in the if statement. The scope of that variable will be limited to if/else statement.

if random := rand.Intn(10); random<3 {
  fmt.Println("our number is < 3")
} else if random<7 {
  fmt.Println("our number is <7")
} else {
  fmt.Println("our number is >= 7")
}

golang 7 - switch syntax

YOUTUBE VIDEO TUTORIAL

Similar to if statments in Go, the variable being evaluated by an if statement is not place within ()s. Also, Go switch statements do not require the break keyword to avoid falling through into the next case. This is great as it eliminates a whole class of errors that have caused switch statements to be shunned.

os := "OS X"
switch os{
case "OS X":
  fmt.Println("MAC.")
case "linux":
  fmt.Println("Linus T's system.")
default:
  fmt.Printf("%s.", os)
}

If for some reason you do want one case to fallthrough into the next you can use the fallthrough keyword.

os := "OS X"
switch os{
case "OS X":
  fmt.Println("MAC.")
  fallthrough
default:
  fmt.Printf("Say hi every time!")
}

Again, like the if statement, you can declare and initialize a variable right in switch line. The variable will be limited to the scope of the switch statement.

switch os := "OS X"; os {
case "OS X":
  fmt.Println("MAC.")
case "linux":
  fmt.Println("Linus T's system.")
default:
  fmt.Printf("%s.", os)
}

Lastly, you do not have to give a switch statement a variable to evaluate at all. In this circumstance, conditions will be evaluated for each case, essentiall making this form of a switch statement an alternative to an if/else statment.

os := "OS X"
switch {
case os=="OS X":
  fmt.Println("MAC.")
case os=="linux":
  fmt.Println("Linus T's system.")
default:
  fmt.Printf("%s.", os)
}

golang 8 - structs

YOUTUBE VIDEO TUTORIAL

Basic struct syntax is straight forward.

type vertex struct{
  int x
  int y
  int z
}

We can also use some of the same shorthands that we use for declaring variable when we declare fields.

type vertex struct{
  int x, y, z
}

We can use dot notation to set the values of the fields. Additionally, we can use {} to instatiate a struct upon declaration.

func main(){
  var vertA vertex
  vertA.x = 2
  vertA.y, vertA.z = 3, 5 
  vertB := vertex{4,6,10}
  
  /// do something with vertA and vertB .....
}

If we are importing a package, any struct given a type name that starts with a capital letter will be exported (available to be used). Similarly, only those fields of the struct that start with capital letter will be expoerted.

////in vertex.go
type Vertex struct{
  int X
  int Y
  int Z
  int t  //this field would not be exported
}

Use dot notation with the name of the package to refer to the imported type.

////in main.go
package main

imports "vertex"

func main(){
  var vert vertex.Vertex
  vert.X = 12
  vert.t = 99 //THIS WOULD YIELD AN EROR
  
  /// do something with vert .....
}

golang 9 - moar structs

YOUTUBE VIDEO TUTORIAL

In additiona to the {} shorthand for declaring and initializing structs I mention above, you can also initialize a struct by specifically referencing a field. By initializing structs this way, un-initiailzed structs will be initialized with their default value (0.0 for a float64).

type vertex struct{
  x, y float64
}

func main(){
  vertA := vertex{3.0, 4.0} // is equivalent to...
  vertB := vertex{x:3.0, y:4.0}
  
  vertC:= vertex{x:3.0} // y field would be 0.0, whereas....
  vertD:= vertex{3.0} // would cause an error
}

Go is not (really) an object oriented language, but structs can embed other struct types, which bears some resemblance to inherittance in object oriented languages. (Methods and Interfaces make structs even more OO-like.)

type vertex struct{
  x, y float64
}

type vertex3D struct{
  vertex
  z float64
}

To set the value of the fields of an embedded type, you use dot notation. Specifically stating the type of the embedded struct is optional.

  var vert3D vertex3D
  //these both work
  vert3D.vertex.x = 2.0  
  vert3D.y = 4.0

You can also use := operator shorthand to declare and initialize a struct that contains an embedded struct.

  vert := vertex{1.0,2.0}
  // all the fields of these 3 vertex3D variable will have the same values
  vert3D_A := vertex3D{vert, 3.0} 
  vert3D_B := vertex3D{vertex:vert,z: 3.0 
  vert3D_C := vertex3D{vertex:vertex{1.0, 2.0}, z:3.0}

golang 10 - methods

YOUTUBE VIDEO TUTORIAL

Another feature of Go structs that make them object-oriented-like is methods. In Go you can attach methods to structs. Method declaratin syntax is similar to function syntax, with the key difference being that after the func keyword you must indicate what struct type the method is being attached to (in this case, myStruct).

type myStruct struct{
  intField int
}
  
func (ms myStruct) myMethodName(param1 int, param2 int) int{
  return ms.intField + param1 + param2
}

Methods are called on a struct using dot notation.

func main(){
  sumert := myStruct{1} // sumert is a variable of type myStruct, which we initialize here
  var retVal int
  retVal = sumert.myMethodName(3,4) // retVal will equal 8
}

Method can also act on structs in a "by value" or "by reference" manner. That is to say, a by reference method will have the ability to make changes to the fields of struct even after the method has returned.

func (ms myStruct) addByValue(x int) { ///it is OK to leave the return type off a function or method if we are not returning a value
  ms.intField+=x
  fmt.Println("ByValue internal value ", ms.intField)
}

func (ms myStruct) addByReference(x int) { 
  ms.intField+=x
  fmt.Println("ByReference internal value ", ms.intField)
}

func main(){
  myVar := 1
  myVar.addByValue(3) 
  fmt.Println("main func value ", myVar)
  myVar.addByReference(3)
  fmt.Println("main func value ", myVar)
}

This code will yield the following output. Note that changes made to field within a "by value" method will not persist. "By reference" changes will.

ByValue internal value 4
main func value 1
ByReference internal value 4
main func value 4

Go is smart enough to tell what type of method is being called without the coder specifically having to caste the struct variable (or even pointer to a struct variable) as either a struct or pointer to a struct. Go will automatically sort out if a variable needs to be dereference or not without throwing a compile error.

func main(){
  myVar := myStruct{1}
  myPtr := &myStruct{2}  // a pointer to a struct
  //all these methods calls will work
  myVar.incByReference(3)
  (&myVar).incByReference(3)
  myPtr.incByValue(3)
}

golang 11 - interfaces

YOUTUBE VIDEO TUTORIAL

Go isn't a traditionally Object Oriented language, and thus lacks the feature of inheritance. Specifically, a struct (the "child") that embeds another struct (the "parent") cannot be set to or passed as a variable of the type parent. For example,

struct car type{
  cost float
}

func (c car) canStart() bool{
  return true
}

struct delorian type{
  car //embed the car struct
  coolnessFactor int
}

func main(){
  var myCar delorian
  myCar.cost = 10000.0
  bool starts = myCar.canStart() //this works, variable of type kid can access parent fields and methods
  var genericCar car
  genericCar = myCar //ERROR! this does not work, embedding a type does not yield OO inheritance
  
}

Instead, Go provide for a feuture call interfaces. An interface is a type. An interface is a type. Interface... type. An interface is a type that declares itself as being able to handles a set of methods. Interfaces do not pertain to fields at all (unlike OO objects which define a set of methods AND properties). So if we were to refactor the above code for interfaces, we would get...

struct car interface{
  canStart() bool{
}

struct delorian type{
  coolnessFactor int
}

func (d delorian) canStart() bool{
  return false
}

func main(){
  var myCar delorian
  myCar.cost = 10000.0
  bool starts = myCar.canStart() //this works, variable of type kid can access parent fields and methods
  var genericCar car  
  genericCar = myCar //this works. delorian impliments the method canStart so it satisfies the conditions required to be type car  
  
}

Without interfaces the type system in Go would be extremely constrictive.

golang 12 - closure

YOUTUBE VIDEO TUTORIAL

There is also closure in go! In Go, functions are first class which means we can have function types, pass fuctions as parameters and return functions to and from functions and methods, and have closure. Lets define a counter function that returns a function. The function that it returns will inciment a count variable, theCount. theCount will take no parameters and return no value itself.

func Mycounter(initialCount int) func() {
	theCount := initialCount
	incriment := func() {
		theCount++
		fmt.Println("The count is", theCount)
	}
	return incriment
}

It's important to note that the scope of the incriment function is the scope of the function that it is decalred within. That is to say, the variabel theCount is within the scope of the function incriment. That means that even after Mycounter() returns, theCount variable will resist in incriment's closure. We can use this as follows.

func main() {
	myFunc := Mycounter(10)
	for i := 0; i < 5; i++ {
		myFunc() //use () to exicute incriment
	}
}

This will give the following output.

The count is 11
The count is 12
The count is 13
The count is 14
The count is 15

Of course in Go we can also return multiple value, including multiple func variables. Let's impliment a getter funtion for our count variable. This function will take no parameters and return an int, thus it's type will be func() int. Note that this is a completely different type than our incriment func variable, which had a type of func(). (Let's also add a paramater to Mycounter so we can set our counters initial value.)

func Mycounter(initialCount int) ( func(), func() int){
  theCount:= initialCount
  incriment := func(){
    theCount++
    fmt.Println("The count is", theCount)
  }
  get := func() int{
    fmt.Println("The count is", theCount)
    return theCount
  }

  return incriment, get
}

This could be used as follows...

func main(){
  incriment, get  := Mycounter(10)
  for i:=0; i<5; i++{
    incriment()
  }
  fmt.Println("The final value is ", get())
}

... yielding this output.

The count is 11
The count is 12
The count is 13
The count is 14
The count is 15
The count is 15
The final value is  15

Notice that incriment and get functions share the same theCount value. It persists encapsulated in Closureland!

Lastly, the declaration for our Mycounter function is starting to get a little long and ugly. Of course, it could get even worse too. Let imagine a method declaration,

func (m myStruct) uglyMethod(paramA, paramB, paramC int) ( func(pA,pB) float, func(p1, p2, p3) int){
  ....
}

That's getting a little dense. To clarify thing we could could declare some new function types.

type firstFuncType func(pA,pB) float
type secondFuncType func(p1, p2, p3) int
func (m myStruct) uglyMethod(paramA int, paramB, paramC) ( firstFuncType, secondFuncType ){
  ....
}

That is a little more readable, in particular if we end up reusing those new types multiple times in our code.

to do

  • if-else statements
  • switch statements
  • structs
  • arrays
  • slices
  • maps
  • defers
  • methods
  • interfaces
  • errors
  • closure
  • variadic functions
  • go routines
  • channels
  • select
  • json/xml
  • go command line tools
  • "go translated"... take confusing go syntax, and explain it
  • MOAR!, make a suggestion below

###sources

© All rights reserved by Alexander White.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment