Go Language in a Nutshell ------------------------- Abbreviated notes based on the tutorial on golang.org, by sussman. (Tutorial is at http://golang.org/doc/go_tutorial.html) Preliminary ----------- * All sources files are UTF-8. String constants too. * Comments are just like /* C */ or // C++ * Semicolons exist at the end of each line, but are OPTIONAL; they're inserted automatically by compiler. Most people leave them off, so the code looks more python-like. * Every source file must have a "Package" statement to declare what package it's part of: package mylibrary * Use "import" to import other packages: import "foo" import bar "foo" // same package imported with name 'bar' * Functions declared with 'func'; must declare a 'main' func inside a 'main' package somewhere in order to get a runnable program: func main() { * Curly {} braces must open on *same* line, not on newlines, else bad things may happen. * Useful os.Stdout library: os.Stdout.WriteString() * Types are always listed *after* symbol names, not before, e.g. "foo int" * Public/private symbols are based on capitalization!! foo // private to package Foo // usable by external users of package * Formatting: tabs are used to indent, not spaces, but the language isn't white-space sensitive. 'gofmt' program reformats all your code according to the One Standard Style, so no holy wars can break out over style. Variables and Constants ----------------------- * var name type = initialval var foo string = "hello" foo := "hello" * const bar = "blah" const bignum = 129839 // LARGE PRECISION integer constants are default * assignment is normally copy-by-value. --> but maps, slices, channels work by reference (see below) * enumerated types are done with 'iota' keyword. 'iota' starts at 0 and increments to the next integer each time it's used; resets when 'const' appears again. e.g. const ( foo = iota // 0 bar = iota // 1 baz = iota // 2 ) * to forcibly allocate a variable, use 'new': foo := new(type) // returns *type pointer * to allocate a map/slice/channel use 'make': foo := make map[string]int // good, returns a map object var foo map[string]int // BAD, creates reference to nil Types ----- * Basics: int, int8, int16, int32, uint32, etc. float, float16, etc. string * Strings are IMMUTABLE! think of them as always 'const string'. * Pointers: var foo *string = &othervar *foo = "new value" * Arrays: var myarray [10]int // declare an array of ints var otherarray [3]int{17, 21, 93} // with initial val constructor var fooarray [...]int{9, 8, 7, 6} // compiler counts size for you var fooarray []int{9, 8, 7, 6} // even simpler notation foo := myarray[5] // get a value out of array size := len(myarray) // len is a builtin function, works on // arrays, slices, maps, strings, channels * Array SLICES (pointers to chunks, just like python) myarray[3:7] // refers to positions 3-6 in myarray myarray[:] // refers to whole array func myfunc(foo []int) { // function takes an integer slice named foo * Maps (hashtables) -- mapping type1 keys to type2 values: mymap := make map[string]int mymap := map[string]int{"yum":6, "oof":23} * Custom types / structures type MyType struct { foo type1 bar type2 } n := new(MyType) // explicit allocation n := MyType(3, "hi") // easier syntax --> Structures can (loosely) contain *methods* as well, but aren't declared in the type definition. See below for details! Loops & Conditionals -------------------- * Basic 'for' loop: for i := 0; condition; i++ { } for { // infinite loop ... return } * 'range' works for looping over slices, maps, etc: for i, v := range thing { // i gets index, v gets value } range also loops over characters in a string. range in a for-loop is a nice way to keep reading from a channel (see below). * 'switch' is much like C, but every case block has implicit 'break' at the end. switch foo { case 0, 1, 2, 3: s1() case 4, 5, 6, 7: s2() default: s3() } * if and switch statements can optionally start with initializers: if foo := func(blah); foo > 10 { } Functions --------- * standard declaration is list of input args, list of output args func funcname(foo1 type1, foo2 type2) (bar1 type3, bar2 type4) { } * multiple returns are just like python's: return foo, bar foo, bar := func() * attaching a method to a custom structure type: --> put a 'receiver' variable at the front of the func declaration: func (floo *MyType) myfunc(inputargs) (outputargs) { // this function can now 'receive' a MyType and reference // it via floo. floo.count += 3 } --> other code can now call the method as if it were a member of the structure: bloo := MyType(3, "hi") bloo.myfunc() this syntax allows us to "attach" a method to almost anything. * anonymous functions are supported, called "function literals", which are also proper closures. * functions are 1st-class: a function can use other functions as inputs and outputs. Interfaces ----------- * There are no formal classes or OO inheritance; instead, we use the "interface" keyword to define a set of methods (a.k.a. vtable). Duck-typing is at work here: if a data type just happens to have those set of methods, then it implements the interface! type reader interface { Read(b []byte) (ret int, err os.Error) String() string } ==> we can now pass 'reader' around as an input parameter to a function. Makes dependency injection trivial. Concurrency ----------- * Data pipes are the synchronization method, not shared memory and mutexes. "Do not communicate by sharing memory; instead, share memory by communicating." * "goroutines" are first-class primitive execution threads that run concurrently and communicate with each other by pushing data through pipes ("channels"). This is very much like the "grep | awk | sed" command-line paradigm. * syntax: go func(); ==> equivalent to shell command "func &". runs in background, exits silently when done. ==> you typically get the result back from a channel, see below. * channels are created with 'chan' type: c := make chan(int) // unbuffered channel which transmits integers d := make chan(int, 2) // same, but buffers 2 integers The unbuffered channel is *synchronous*, meaning an attempt to read or write from the channel will block until the complementary read/write happens on the other end! * using channels: c <- 3 // push 3 into channel c. (may block if no buffer) <-c // the next value coming out of channel c; blocks. close(c) // no more vals will be sent in c; reading instantly returns 0