Skip to content

Instantly share code, notes, and snippets.

@smileisak
Forked from honkskillet/byte-sizetuts.md
Created December 25, 2017 19:12
Show Gist options
  • Save smileisak/45cdcfdf2df149e5b64bde693e5e4999 to your computer and use it in GitHub Desktop.
Save smileisak/45cdcfdf2df149e5b64bde693e5e4999 to your computer and use it in GitHub Desktop.
A series of golang tutorials with youtube videos.

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 for 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 don't care about the element, we can 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() func(){
  theCount:= 0
  increment := func(){
    theCount++
    fmt.Println("The count is", theCount)
  }
  return increment
}

It's important to note that the scope of the increment 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 increment. That means that even after Mycounter() returns, theCount variable will resist in increment's closure. We can use this as follows.

func main(){
  var incFunc func()
  incFunc = MyCounter()  
  for i:=0; i<5; i++{
    incFunc()  //use () to execute increment
  }
}

This will give the following output.

The count is 1
The count is 2
The count is 3
The count is 4
The count is 5

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 increment 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
  increment := func(){
    theCount++
    fmt.Println("The count is", theCount)
  }
  get := func() int{
    fmt.Println("The count is", theCount)
    return theCount
  }
  return increment, get
}

This could be used as follows...

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

... 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.

golang 13 - arrays

YOUTUBE VIDEO TUTORIAL

Array declaration syntax is straight forward. We have to decalre both the value of the elements of the array AND the length. All the declaration below declare arrays of type [5]int. Note, for arrC, Go will infer the length of the array if we use [...] during assignment.

  var arrEmpty [5]int
  var arrA = [5]int {1,2,3,4,5}
  arrB := [5]int {3,4,5,6,7}
  arrC := [...]int {4,5,6,7,8}

I repeat, the type of an array consists of both it's length And it's element type. This won't work....

  arrA := [5]int {1,2,3,4,5}
  arrB := [4]int {4,5,6,7}
  arrA=arrB //// ERROR! arrA and arrB variables of different types, specifically [5]int and [4]int respectively

The other crucial thing about Go arrays is that an array is a value, not a pointer. In C, an array is a pointer to block of memory. Not so in Go. If you set a Go array arrA equal to another Go array arrB, the two arrays will represent values in two completely differnt blocks of memory. Any subsequent changes to arrA will have no effect on arrB.

func main(){
  var arrA [5]int
  var arrB = [5]int {3,4,5,6,7}
  arrA=arrB //the values of arrB are copied into the memory of arrA
  arrA[2]=15  //arrB will be uneffected
  fmt.Println(arrA,arrB)
  fmt.Printf("%p \n%p\n",&arrA,&arrB)  //%p is formatting for a point, dereference arrA with & to get a pointer
}

This yields the following output. Notice changes to one array do not effect the other and the arrays occupy different locations in memory.

[3 4 15 6 7] [3 4 5 6 7]
0x2081ba150 
0x2081ba180

golang 14 - slices

YOUTUBE VIDEO TUTORIAL

90%+ of the time the you work with lists of values in Go, you will be working with slices, not arrays. Slices are an abstraction built on top of arrays. The type of a slice indicates the type of the elements. A slice can contain any number of elements, unlike an array.

var mySlice []int  // a slice of ints, length is variable
var myArray [5]int // a array of ints that is 5 elements long

There are the expected ways to declare a slice. Declaration is very similar to array decalartion.

var aSlice []int  //nil slice, len and cap are zero
var bSlice = [int]{1,2,3}
cSlice := [int]{3,4,5}

You can make a slice from an array (or another slice). That is, to take a slice from it. The format is arrayName[startIndexInclusive:endIndexExclusive]. You can leave off one or both of the start or end indices.

myArray := [6]int{1,2,3,4,5,6}
sliceA := myArray[2:5] // value will be {3,4,5}
sliceB := myArray[:3]  // value will be {0,1,2}
silceC := myArray[3:]  // claue will be {2,3,4,5,6}
sliceD := myArray[:]   // value will be {1,2,3,4,5,6}

A slice is actually comprised of 3 parts under the hood: a pointer to an element in an array, a length and a capacity. The length is the number of elements in the slice. The capacity is the total number of elements the slice can have before go needs to reallocate memory to hold the slice. For example, take the following main function.

func main(){  
  arrA := [6]int{1,2,3,4,5,6}
  slcA := arrA[0:3]
  fmt.Println( slcA, len(slcA), cap(slcA))
  fmt.Printf("%p\n%p", &arrA, &slcA[0])
 }

The "%p" formatting allows us to print out the values of pointers. & gets of the pointer for a value. In this case we get the pointers to the start of the array and the first element of the slice. This will generate the output...

[1 2 3] 3 6
0x2081ba150
0x2081ba150

The slice that we made has a length of 3 and a capacity of 6 (the size of the underlying array). The pointer values for the start of the array and first element of the slice are identical (for now). Currently, Go is using our array as the underlying storage for our slice.

We can append elements to the end of a slice using the append function. Lets append a bunch of elements in a for loop.

func main(){  
  arrA := [6]int{1,2,3,4,5,6}
  slcA := arrA[0:3]
  fmt.Println( "\n",arrA,"\n",slcA,"\n")  //print out the value of both our array and  slice
  for i:=0;i<10;i++{
    slcA = append (slcA,90+i)  //append an int to the end of our []int slice
    //print out the address of the first element of the slice, the length, the capacity and the value of the slice
    fmt.Printf("P:%p L:%2v C:%2v  S:%v\n",   &slcA[0],len(slcA), cap(slcA),  slcA,)  
  }
  fmt.Println( "\n",arrA,"\n",slcA)  //print out the final value of our array and slice 
}

Will render output...

 [1 2 3 4 5 6] 
 [1 2 3] 

P:0x2081ba150 L: 3 C: 6  S:[1 2 3]
P:0x2081ba150 L: 4 C: 6  S:[1 2 3 90]
P:0x2081ba150 L: 5 C: 6  S:[1 2 3 90 91]
P:0x2081ba150 L: 6 C: 6  S:[1 2 3 90 91 92]
P:0x2081c8000 L: 7 C:12  S:[1 2 3 90 91 92 93]
P:0x2081c8000 L: 8 C:12  S:[1 2 3 90 91 92 93 94]
P:0x2081c8000 L: 9 C:12  S:[1 2 3 90 91 92 93 94 95]
P:0x2081c8000 L:10 C:12  S:[1 2 3 90 91 92 93 94 95 96]
P:0x2081c8000 L:11 C:12  S:[1 2 3 90 91 92 93 94 95 96 97]
P:0x2081c8000 L:12 C:12  S:[1 2 3 90 91 92 93 94 95 96 97 98]
P:0x2081cd250 L:13 C:24  S:[1 2 3 90 91 92 93 94 95 96 97 98 99]

 [1 2 3 90 91 92] 
 [1 2 3 90 91 92 93 94 95 96 97 98 99]

Notice a few things.

  • The original capacity of our slice matches the original size of our array.
  • The location in memory of the values of our slice stays the same until the slice's length exceeds the slice's capacity. At this point, under the hood, Go allocates a new array acciated with a larger contiguous block of memory. The slice's capacity is doubled. Subsequent append calls won't require new memory allocation until the capacity is again exceed (the last append call also exceeds capacity)
  • The first 3 append calls overwrote the final 3 elements of our original array variable! So be careful when working with both arrays and slices. You may be better off just working with slices.

Lastly,consider this. The call slcA[0]=-5 will a have different effect depending of where it is made. If we call it before the for loop, when our array and our slice are sharing memory, our final values will be...

 [-5 2 3 90 91 92] 
 [-5 2 3 90 91 92 93 94 95 96 97 98 99]

If we call it after the loop, when slice is using a different block of memory, the final values will be...

 [1 2 3 90 91 92] 
 [-5 2 3 90 91 92 93 94 95 96 97 98 99]

golang 15 - variadic functions

YOUTUBE VIDEO TUTORIAL

A variadic function in Go is a function that can take a variable number of parameter. An example of a variadic function is fmt.Println(). The variardic parameter is declared with the format parameterName ...elementType. parameterName would then be available as a slice of type []elementType within the body of the function. For example,

func vardiacFun( params ...int){
  fmt.Printf("Parameter type: %T\n",params)  
  fmt.Println(params)
  for _,elem :=range params{ //loop of the parameter
    fmt.Println(elem)
  }
}

func main() {
  vardiacFun(1,3,4,5,6)
}

This would yield the following output.

Parameter type: []int
[1 2 3 4 5]
1
2
3
4
5

A variadic function can have more than just the variadic parameter, but the variadic parameter must be the last parameter.

func vardiacFun( s1, s2 string, params ...int){ //variadic parameter must come last
  //do something here
}

If it is your preference, you can always avoid using a variadic function by using a normal slice as a parameter. We can rewrite our first example in a nonvariadic style as below. The bodies of variadic() and nonvariadic() are identical. The differences are in the functions' declarations and how they are called in main().

func nonVardiacFun( params []int){
  fmt.Printf("%T\n",params)
  fmt.Println(params)
  for _,elem :=range params{
    fmt.Println(elem)
  }
}

func main() {
  nonVardiacFun([]int{1,2,3,4,5})
}

golang 16 - maps

YOUTUBE VIDEO TUTORIAL

In Go, in addition to arrays and slices, there is a collection that stores key/value pairs. In Go this is called a map. Other languages sometimes call these hashes, dicts or dictionaries. The type of a map includes the type of the key and type of value. Declaration and initialization syntax is as follows.

var aMap map[string]int  //where string is the type for the key and int is the type for the value
aMap = map[string]int{}  //set aMap equal to an empty map
bMap := map[string]int{} //shorthand

You can add a key/value pair to a map using []s.

aMap["a"]=1  //aMap now contain the value 1 for the key "a"

Use the built in delete function to remove a key/value pair. The first parameter is the map variable, the second is the key.

delete(aMap,"a")

Note that if you try to retrieve a value for a key that does not exist in a map, the value that is returned will be equal to the default for the value type. That is, if the values in your map are ints, you'll get back 0 for a non existant key. If the values are string, you'll get back "". The trouble is, the default value is always a valid value. To clarify whether a key/value pair exists, retrieval from a map actually returns two values. First, the value for the key. Second, a boolean which indicates whether the key exists in the map. For example,

func main() {
  myMap := map[string]int{}//declare a map and initialize to an empty map
  //check to see if there is a key value pair for key "a"
  val, ok := myMap["a"] //val is an int, ok is bool
  fmt.Println(val, ok)
  //add some key value pairs
  myMap["a"]=0
  myMap["b"]=1
  myMap["c"]=2
  //use if statement to check to see if there is a key value pair for key "a"	
  if val, ok = myMap["a"]; ok{
    fmt.Println("there is a value for the a key")
    fmt.Println(val, ok)
  }
}

Notice it is convenient to retrieve the value for a key and the existance of a key/value pair within a Go for statement. The output for the above code would be...

0 false
there is a value for the a key
1 true

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