06 July 2014

IDE

eclipse with Go plugin, IntelliJ with Go plugin

Go Type

string, int, bool, float …. 1. Array 2. Slice 3. Struct 4. Pointer 5. Function 6. Interface 7. Map 8. Channel

Basic Syntax

define a variable

var message string
message = "Hello Go World"

go is special, if you declared a variable without use it. you will get an error.

define multiple variable

var a, b, c int
fmt.Println(a,b,c)

define a variable and initialize value

var msg string = "hello"
var a,b,c int = 1,2,3

type infer, don’t need to specify the type

var msg = "hello"
var a,b,c = 1,false,3

short cut for declare and initialize

message := "hello"
a,b,c := 1,2,3

Pointer

in go, string pointer can only point to a string while an int pointer can only point to a int variable

parameter pass into function is pass by value by default, it is a copy of the variable, if a pointer pass into the function, the pointer it self is a copy but it still point to the original variable, this is like c#

define a pointer

msg := "hello"
var message *string = &msg

now message is a pointer to variable msg print the message will print the address of the msg variable

in order to print the value of the msg, use * to dereference the variable

fmt.Println(*message)

*message = "hi"
fmt.Print(msg)  //print hi

if assign a value to pointer, it will change the reference value of the variable

No Class in Go

yes there is no Class

User Defined Types

define a user defined types that behave like string

type Salutation string

we just defined a type called Salutation that same as string

instead of use a string, let’s create something useful

type Person struct {
	firstName string
    lastName  string
}

use a type

var s = Person{"sean", "xu"}
var sean = Person{lastName:"xu", firstName:"Sean"}
s.name = "Anna"

in go, Capitalize means Public, in this example Person is a public type. visiblity is base on the nameing conversion

Constants

define a constant

const (
	PI = 3.14
	Language = "GO"
)

fmt.Print(PI)
fmt.Print(Language)

functions

function in Go can have multiple return values can use like any other type closure

basic function declaration

func SayHello(person Person) {
	fmt.Println(person.firstName)
	fmt.Println(person.lastName)
}

function with return value

func CreateMessage(firstName, lastName string) string {
	return firstName + " "+lastName
}

firstName, lastName string is a shortcut for define two string parameters, and this function has a return value of string.

multiple returns

//multiple return value

func CreateMessageWithMultipleReturn(firstName, lastName string) (string, string) {
	return firstName + " "+lastName, "Hey!" + firstName
}

how to use

hellomsg, alter := CreateMessageWithMultipleReturn(sean.firstName,sean.lastName)
fmt.Println(hellomsg,alter)

if we just need hellomsg and don’t care what alter return we can use _ to ignore the unused value

hellomsg, _ := CreateMessageWithMultipleReturn(sean.firstName,sean.lastName)
fmt.Println(hellomsg,alter)

named return value

func CreateMessageWithNamedReturn(firstName, lastName string) (message string, alternate string){
	message = firstName + " "+lastName
	alternate ="Hey!" + firstName
	return
}

sometimes it will be confused when there are multiple return values, then we can use named return value

variadic functions

this is same as params in csharp

//Variadic functions

func CreateMessageWithVariadic(name string, message ...string) string {
	len := len(message)
	return name + " " + strconv.Itoa(len)
}

in go convert int to string need another package strconv

need to import that package

import (
	"fmt"
	"strconv"
)

strconv.Itoa(len)
strconv.Atoi(str)  // convert str to int

pass a function

in go function can be passed as a parameter, same as function pointer in c and delegate in csharp, or call back in javascript

func SayHello(do func(string))

here do is the parameter name, and its’ type is func and has one string parameter with no return value

func GreetWithFunction(do func (string)) {
	do("hello")
}

func Print(message string) {
	fmt.Println(message)
}

func PrintLn(message string) {
	fmt.Println(message)
}

GreetWithFunction(Print)

declaring a function type

// this is a function take string as parameter return nothing
type Printer func(string)
// this is a function take string as parameter return two string 
type Printer func (string) (string,string)

then we can replace the previous demo with func GreetWithFunction(do Printer) { do(“hello”) }

Closures

we can also return an function from another function. and the inner function can still access to the variable of the outer function even the outer function has finished, this is called Closures

func PrintCustom(custom string) func(string) (string,string) {
	return func(s string) (string ,string) {
		fmt.Println(s + custom)
		return "1","2"
	}

}

func GreetWithFunction(do Printer) {
	do("hello")
}

GreetWithFunction(PrintCustom("sean"))

type Printer func(string) (string ,string)

we can also replace the func return type to Printer

func PrintCustom(custom string) Printer {
	return func(s string) (string ,string) {
		fmt.Println(s + custom)
		return "1","2"
	}

}

flow control

if statement

syntax

if (optional statment); condition { block }

if isBool {
	// do sth
}

if isBool := true; isBool {
	// do sth
}

isFormal := true
if prefix := "Mr "; isFormal {
	//do sth
} 

in this example, variable prefix will only exists in the if block.

switch case

in go cases can be expressions

switch basic

in go, switch by default will not fall through, that means, statement will break once the case expression matched. if we really need fall through, we can use fallthrough keyword

func GetPrefix(name string) (prefix string) {
	switch name {
	case "Bob":
		prefix = "Mr "
	case "Joe", "Amy":
		prefix = "Dr "
	case "Mary":
		prefix = "Mrs "
	default:
		prefix = "Dude "

	}
	return
}

// fall through demo
func GetPrefix(name string) (prefix string) {
	switch name {
	case "Bob":
		prefix = "Mr "
		fallthrouth
	case "Joe":
		prefix = "Dr "
	case "Mary":
		prefix = "Mrs "
	default:
		prefix = "Dude "

	}
	return
}

switch on nothing, it’s really flexible

func SwitchOnNothing(name string) (prefix string) {
	switch {
	case name == "Bob":
		prefix = "Mr "
	case len(name) == 10:
		prefix = "Dr "
	default:
		prefix = "Dude "
	}
	return
}

switch on type

func SwitchOnType(x interface {}) {
	switch t := x.(type) {
	case int:
		fmt.Println("int")
	case string:
		fmt.Println("string")
	default:
		fmt.Println("unknow")
	}
}

loop

there is only one loop that is for basic for loop

func ForLoop(times int) {
	for i := 0; i< times; i++ {
		fmt.Println(i)
	}
}

func WhileLoop(times int) {
	i := times
	for i < times {
		fmt.Println(i)
		i++
	}
}

infinite loop

for {
	fmt.Println("true")
}

end the loop with break

for {
 		if true {
		break;  // or continue
	}
	fmt.Println("true")
}

Range

Range can used on

Array or slice String Map Channel

basic usage

func RangeDemo2() {
	slice := []greeting.Person{
		{"sean", "xu"},
		{"anna", "shen"},
	}
	Greet2(slice)
}

func Greet2(person []greeting.Person) {
	for _, s := range person {
		fmt.Println(s.FirstName)
	}
}

Map

prefixMap := make(map[string]string)

var prefixMap map[string]string
prefixMap = make(map[string]string)

the string in side [] is the type of key, and the string after [] is the type of value

prefixMap["Bob"] = "Mr "
prefixMap["Joe"] = "Dr "

this will insert the value into the map

shorthand to create a map, notice we need the last ,, or it doesn’t compile

prefixMap := map[string]string {
	"Bob" : "Mr ",
	"Joe" : "Dr ",
}

delete an element from map

delete(prefixMap, "Bob")

check element exists in the map

if value, exists := prefixMap[name]; exists {
	return value
}

the value is the value of the key in the map, the exists is a bool value, indicate whether the key is in the map. prefixMap[name] actually return two value

Array

an Array is a collection has a fix size

var arr [3]int  //define an array

array is fixed size no initialization not a pointer

Slice

var s []int = make([]int,3,5)

the slice is basically a pointer to an array, it is a wrapper to array, the syntax to initilize slice is quite similar as array, besides you don’t need to specify the size.

slice is a pointer or we can say it is a reference type

a slice is fixed size use make to initialize otherwise is nil points to an array

	var s []int
	s = make([]int,3)
	s[0] = 1
	s[1] = 10
	s[2] = 500
	s[3] = 23  // will be error because the length and cap of slice is 3

	//short
	s := []int { 1,2,3 }
	
	slice := []greeting.Person{
		{"sean", "xu"},
		{"anna", "shen"},
	}

	slice = slice[0:1]
	slice = slice[:2]
	slice = slice[1:]

to slice a slice we use the syntax slice[0:1] mean that from 0 to 1, and include 0 but exclude 1 (>=0 <1) that will return {“sean”, “xu” } slice[:2] means take to index 2 but don’t incluce 2, slice[1:] means take from index 1 and include 1

append a slice return a new slice

slice = append(slice, greeting.Person{"jack","xu"}

and we can also append another slice to it

slice = append(slice, slice...}

deleting from a slice

slice = append(slice[:1],slice[2:]...)

this will delete the second item from the slice

	func SliceDemo() {
		slice := []int {1,2,3,4,5}
	
		slice = append(slice[:2],slice[3:]...)
	
		for _, v := range slice {
			fmt.Print(v)
		}

	}

Method

there is no class in go, method in go is append to type

first, we need have a named type

eg: we are going to add a method IntruduceMySelf under a slice of Person struct, first, we need to create a named type People, which is a slice of Person

then use the method syntax

type People []Person

func (people People) IntruduceMySelf() {
	for _, p := range people {
		fmt.Println("hi my name is "+p.FirstName)
	}
}

usage

people := greeting.People {
	{"Sean", "xu"},
	{"Anna", "shen"},
}

people.IntruduceMySelf()

Method with a Pointer Receiver

parameter is passed by value, so if you want to change the value of the type your method append, you must use Pointer Receiver

func (person *Person) Rename(newName string) {
	person.FirstName = newName
}

usage

people := greeting.People {
	{"Sean", "xu"},
	{"Anna", "shen"},
}

people[0].Rename("Jack")

interface

in go, interface is just a contact, and as long as your object match the contact, that means your type implements this interface, you don’t have to explicit implement the interface

create interface

type Renamable interface {
	Rename(newName string)
	CanRename() bool
}

func (person *Person) Rename(newName string) {
	person.FirstName = newName
}

func (person *Person) CanRename() bool {
	return true
}

we are creating an interface Renamable that has two methods, Rename and CanRename, then we add two methods on the type Person

usage:

func RenameToSth(r greeting.Renamable){
	if canRename := r.CanRename(); canRename {
		r.Rename("Jack")
	}
}

then we could create a function that the interface Renameable as parameter, and we could pass Person to this function, notice we must pass the pointer of Person, that is the address of Person,

people := greeting.People {
	{"Sean", "xu"},
	{"Anna", "shen"},
}

RenameToSth(&people[0])

Empty interface

interface{}	

if we use an empty interface as parameter, that means you can pass any type of parameter to that function

func GetType(x interface{}) {
	switch t := x.(type) {
	case int:
		fmt.Println("int")
	default:
		fmt.Println("invalid type",t)
	}
}

usage: GetType(people[0]) GetType(1)

example of use interface

func (person *Person) Write(p []byte) (n int, err error) {
	s := string(p)
	person.Rename(s)
	n = len(s)
	err = nil
	return
}

fmt.Fprintf(&people[0], "the count is %d", 10)
fmt.Println(people[0].FirstName)

this is not a good example, but you can understand from the example here, the first argument of fmt.Fprintf is a Writer interface that has only one method Write. and we implement that function under Person, so we can pass person to fmt.Fprintf, and that means, as long as your object implment the Write method and the argument and return type is same as the interface, then you can pass your object to that function, that’s the power of interface

Concurrency

Goroutines

syntax

go function

goroutine kickoff a new thread to run the function, like async in c#

Channels

in go, there is no such concept of lock to sync the data between two thread, instead, go use channel to sync data

syntax

done := make(chan,bool)

this create a channel called done that can only send bool in the channel

go func() {
	doSth()
	done <- true
}()

to pass the channel to the goroutine function, we need to change the signature of the method which we don’t want to do that. luckily, we can use anonymous function to pass the channel to the function.

done <- true

this syntax means to put true into the channel

<- done

this will make our main thread wait until the done channel fulfilled

buffered channel

by default, there can only be one item in the channel at one time, and you can only put the item into channel when there is no item in the channel

to define buffered channel

done := make(chan bool, 2)

by this way we define a buffered channel done with 2 buffer, we can put two item into this channel at one time

close(c)

close a channel and tell the consumer that it has put all the data into the channel

example

func (people *People) ChannelPeople(c chan Person) {
	for _, p := range *people {
		c <- p
	}
	fmt.Println("channel done")
	close(c)
}

people := greeting.People {
	{"Sean", "xu"},
	{"Anna", "shen"},
}

peopleChannel := make(chan greeting.Person);
go people.ChannelPeople(peopleChannel)
for p := range peopleChannel {
	fmt.Println(p.FirstName)
}

result: Sean
		Anna
		channel done

peopleChannel := make(chan greeting.Person, len(people));
go people.ChannelPeople(peopleChannel)
for p := range peopleChannel {
	fmt.Println(p.FirstName)
}

result: channel done
		Sean
		Anna

the keyword range will block the channel, when I use non-buffered channel, eachtime item was put into the channel, it will be read from another side.

when use buffered channel, range will wait the channel until it is full

Select statement



blog comments powered by Disqus