by alexander white ©
- 0 - installation on a Mac
- 1 - packages
- 2 - remote packages
- 3 - variable syntax
- 4 - functions syntax
- 5 - loops syntax
- 6 - if/else syntax
- 7 - switch syntax
- 8 - structs
- 9 - moar structs
- 10 - methods
- 11 - interfaces
- 12 - closure
Youtube: link to playlist
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
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
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
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
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
}
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...
}
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")
}
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)
}
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 .....
}
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}
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)
}
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.
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.
- 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
- Go blog
- tour of Go
- Go by example
- golang docs
- golang weekls
- closure and first class function code walk
© All rights reserved by Alexander White.