- Garbage Collection - Automatic memory management
- Fast to compile and execute
- Simpler Langauge
- Concurrency is efficient
Get started by downloading Go @ https://golang.org/dl/
wget https://go.dev/dl/go1.17.6.linux-amd64.tar.gz
sudo tar -xvf go1.17.6.linux-amd64.tar.gz
sudo mv go /usr/local
vi ~/.bashrc
# Add the following to your .bashrc:
# export GOROOT=/usr/local/go
# export GOPATH=$HOME/dev/go
# export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
mkdir -p /Users/<username>/Development/go/src
vi ~/.bashrc
# Add the following to your .bashrc:
# export GOROOT=//Users/<username>/Development/go
# export GOPATH=/Users/<username>/Development/go
# export PATH=$PATH:/Users/<username>/Development/go/bin
vi ~/.bash_profile
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
cmd | Example | Description |
---|---|---|
go run | compile and run the program | compile and run the program |
go build | compiles the program | compiles the program |
go mod | formats source code files | formats source code files |
go fmt | formats source code files | formats source code files |
go doc | go doc fmt |
prints documentation for pkg |
go test | run test files | run test files |
go get | downloads pkgs and installs them | downloads pkgs and installs them |
go list | list installed pkgs | list installed pkgs |
go get golang.org/x/tools/cmd/goimports
# godoc pkgname
godoc fmt
# godoc pkgname method
godoc fmt Println
# go docs locally
godoc -http=:8000
- 3 Common directories -- src - Contains source code files -- pkg - Contains packages (libraries) -- bin - contains compiled executables
Save the following to github.com<username>\hello\hello.go
package main
import "fmt" # used to access other packages
func main () {
fmt.Printf("Hello World\n")
}
Execute the file
go run hello.go
Build the file to an executable
go build hello.go
Build and install the program
Note that you can run this command from anywhere on your system.
The go tool finds the source code by looking for the github.com/user/hello package
inside the workspace specified by GOPATH or GOROOT.
go install github.com\<username>\hello
hello
If you're using a source control system, now would be a good time to initialize a repository, add the files, and commit your first change.
New-Item -type Directory \src\github.com\<username>\newmath"
# Save the following to github.com\<username>\newmath\sqrt.go
# Package newmath is a trivial example package
package newmath # package declaration to identify what package the file belongs too
# Sqrt returns an approximation to the square root of x
func Sqrt(x float64) float64 {
z := 1.0
for i := 0; i < 1000; i++ {
z -= (z*z - x) / (2 * z)
}
return z
}
go build github.com\<username>\newmath
you can now run: import "github.com/user/newmath" in your programs
Save the following to github.com<username>\newmath\sqrt_test.go
package newmath
import "testing" // import external package
func TestSqrt(t *testing.T) {
const in, out = 4, 2
if x := Sqrt(in); x != out {
t.Errorf("Sqrt(%v) = %v, want %v", in, x, out)
}
}
go test github.com\<username>\newmath
Expression | Value |
---|---|
true && true |
true |
true && false |
false |
false && true |
false |
false && false |
false |
`true | |
`true | |
`false | |
`false | |
!true |
false |
!false |
true |
%d // decimal integer %x, %o, %b // integer in hex, octal, binary %f, %g, %e // float; 3.141593, 3.141592653589793, 3.41593e+00 %t // bool; true or false %c // rune (Unicode code point) %s // string %q // quoted string "abc" or rune 'c' %v // any value in a natural format %T // type of value %% // literal percent sign (no operand)
//******************************************************************************* // Variables - Data stored in memory // Store or hold a value // case sensitive; must begin with a letter' can't use keywords // keyword name type = value //******************************************************************************* var x string = "Hello World" // sequence of bytes stored in unicode var x, y, z int = 1, 2, 3 // integer values var x float = 1.0 // fractional (decimal values) var c, python, java bool = true, false, false var b, f, s = true, 2.3, "four" // bool, float, string var f, err = os.Open(name) // returns a file handle and an error (default nil)
// group variables var ( j = 356.245 k int = 0 inter, floater, stringer = 1, 2.0, "hi" )
/* Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type. */ func main() { x, y, z := 1, 2, 3
fmt.Println(x, y, z)
}
// Using the variable to assign itself var x string x = "Hello " x += "World" // or x = x + "World"
// Try to reduce var scope and combine statements err := r.ParseForm() if err != nil { log.Print(err) }
// Now refactor too if err := r.ParseForm(); err != nil { log.Print(err) }
// Binary operators // Arithmetic: +, -, *, /, %, <<, >> // Comparison: ==, !=, >, <, >=, <= // Boolean: &&, ||
//******************************************************************************* // Variables Scope - where can a variable be accessed in code // Blocks - a sequence of declarations and statements; lexical scoping // - Explicit and Implicit Blocks // -- Universe Block - all Go source // -- Package Block - within a package // -- File Block - all source within a file // -- Matching sequence Block - if, for, switch, func //******************************************************************************* var x int = 4
func f() { fmt.Printf("%d", x) // 4 }
func g() {
fmt.Printf("%d", x) // 4
}
// ---------------------------
func f() {
x := 4 // scoped to func f
fmt.Printf("%d", x) // 4
}
func g() { fmt.Printf("%d", x) // error }
//******************************************************************************* // Pointers - address to data in memory //******************************************************************************* &x // returns the address of a variable or func *x // returns data at the address (dereferencing)
ptr := new(int) // new() func creates a var and returns a pointer to the var *ptr = 3
//******************************************************************************* // Constants // Value is known at compile time //******************************************************************************* // Constants can be character, string, boolean, or numeric values. // Constants cannot be declared using the := syntax. All files in package will // have access. const Pi = 3.14
const ( cycles = 5 res = 0.001 size = 100 )
// Generate const using iota type Grades int const ( A Grades = iota B C D F ) // A is: 0, B is: 1, C is: 2, D is: 3, F is: 4 fmt.Printf("A is: %v, B is: %v, C is: %v, D is: %v, F is: %v", A, B, C, D, F)
//******************************************************************************* // Functions //******************************************************************************* // func funcname (params) returntype; call by value - args are copied to params // Advantages: Data Encapsulation; Disadvantages: Copying time func add(x int, y int) int { return x + y } add(42, 13)
// call by reference - pass a pointer func add(x *int) { *x = *x + 1 } func main() { x := 2 add(&x) fmt.Println(x) // 3 }
// Passing an array by pointer (better to use a slice) func main() { a := [...]int{1,2,3,4,5} // in an array literal ellipsis "..." determines len by the num values printArrayContents(&a) }
// Passing an array by pointer func printArrayContents(x *[5]int) { for i, v := range *x { fmt.Printf("index: %d, value: %d\n", i, v) } }
// Passing a slice by reference func main() { a := []int{1,2,3,4,5} // slice doubleSliceContents(a) // slice passes by reference and doesn't copy the data fmt.Println(a) }
func doubleSliceContents(x []int) { for i, v := range x { x[i] = x[i] + v } }
// variadic func (...) to take variable num of arguments // args treated as a slice inside func // fmt.Println(add(1,2,3)) // or pass a slice fmt.Println(add(slicename...)) func add(args ...int) int { total := 0 for _, v := range args { total += v } return total }
// Return two strings func swap (x, y string) (string, string) { return y, x } swap("hello", "world")
// Don't care about the first string use underscore; val will equal "world" _, val = swap("hello", "world")
// Example program that has a func that aceepts input package main
import "fmt"
func main() { fmt.Print("Enter a number: ") var input float64 fmt.Scanf("%f", &input)
output := input * 2
fmt.Println(output)
}
// Receiver func func (p person) print() { fmt.Println(p) }
// Functions are First-Class and can be treated like any other type /* Variables can be declared with a func type Can be created dynamically Can be passed as arguments and returned as values Can be stores in data structures */
// func as an argument func applyIt(afunc func (int) int, val int) int { return afunc(val) }
func incFn(x int) int {return x+1} func decFn(x int) int {return x-1}
func main() { fmt.Println(applyIt(incFn, 2)) fmt.Println(applyIt(decFn, 2)) }
// Anonymous function (Lambda) func applyIt(afunc func (int) int, val int) int { return afunc(val) }
func main() { v := applyIt(func (x int) int {return x+1}, 2} fmt.Println(v) }
//******************************************************************************* // Types //******************************************************************************* /* Value Type Reference Type Need *pointer to update No pointer needed to update ---------------------------------------------------------------------- int | slices float | maps string | channels bool | pointers struct | functions */
type Celsius float64 // ~15 digits of precision type IDnum int
var temp Celsius = 32.0
// Value Type Example----------------------- type person struct { firstName string lastName string }
john := person{ firstName: "John", lastName: "Doe" }
// Receiver func, w/ pointer to a person func (p *person) updateLastName(newLastName string) { p.lastName = newLastName }
// Receiever func func (p person) print() { // print field name and value fmt.Printf("%+v\n", p) }
// Another Reciever func or method associated with type type myInt int func (mi myInt) Double() int { return int(mi*2) } func main() { v := MyInt(3) fmt.Println(v.Double()) // 6 }
john.updateLastName("Smith") john.print() // {firstName:John lastName:Smith}
// Reference Type Example------------------- mySlice := []string{"Hello"}
func updateSlice(s []string) { s[0] = "Bye" }
updateSlice(mySlice) fmt.Println(mySlice) // Bye
// Type Conversion var x int32 = 1 var y int16 = 2 x = y // error x = int32(y) // success
//******************************************************************************* // If Statement // if { // // } //******************************************************************************* if x > 0 { return y }
// Another example with a test condition if ValidateURL := strings.HasPrefix("http://www.devalex.us", "http://"); ValidateURL != true { url := "http://" + "www.devalex.us" }
// If with a variable assignment. Only in scope until the end of the if/else func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim }
// FizzBuzz for i := 1; i < 100; i++ { if i % 3 == 0 && i % 5 == 0 { fmt.Println("FizzBuzz") } else if i % 3 == 0 { fmt.Println("Fizz") } else if i % 5 == 0 { fmt.Println("Buzz") } else { fmt.Println(i) } }
//******************************************************************************* // Switch Statement //******************************************************************************* switch a { case 0: fmt.Printf("0") default: fmt.Printf("non-zero") }
// or with a expression a, b := x[i], y[j] switch { case a < b: return -1 case a == b: return 0 case a > b: return 1 }
switch i { case 0: fmt.Println("Zero") case 1: fmt.Println("One") case 2: fmt.Println("Two") case 3: fmt.Println("Three") default: fmt.Println("Unknown Number") }
// Using a func func SigNum(x int) int { switch { case x > 0: return +1 case x < 0: return -1 default: return 0 } }
// Another func example func printValue(v interface{}) { switch v := v.(type) { case string: fmt.Printf("%v is a string\n", v) case int: fmt.Printf("%v is an int\n", v) default: fmt.Printf("The type of v is unknown\n") } }
//******************************************************************************* // For Loops //******************************************************************************* /* for initialization; condition; post { // initialization - executes before the loop starts; typically a var // declaration, assigment statement, or func call // condition - boolean expression; loop ends when it evals to false // post - executes after the body of the loop // zero or more statements } */ sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum)
// For is also used as a traditional while loop /* for condition { // zero or more statements } */ sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum)
//******************************************************************************* // Structs // Sequence of named elements, called fields, each of which has a name and a type // //******************************************************************************* type Vertex struct { X int Y int }
// Access struct fields w/ dot func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X)
// or
v := new(Vertex)
fmt.Println(v) // 0 0
v.X, v.Y = 11, 9
fmt.Println(v) // 11 9
// or preferred
v := Vertex{
X = 1,
Y = 2
}
fmt.Println(v)
}
// Another example for completness package main
import ( "fmt" )
type Employee struct { ID int Name string Position string Salary int }
func main() { var dilbert Employee dilbert.ID = 1 dilbert.Name = "Dilbert" dilbert.Salary = 50000
fmt.Println(Bonus(&dilbert, 10))
fmt.Printf("dilbert salary before raise is %d\n", dilbert.Salary)
dilbert.AwardAnnualRaise()
fmt.Printf("dilbert salary after raise is %d\n", dilbert.Salary)
}
func Bonus(e *Employee, percent int) int { return e.Salary * percent / 100 }
// Receiver func; add method to Employee struct func (e *Employee) AwardAnnualRaise() { e.Salary = e.Salary * 105 / 100 }
/* Use struct literal initialization to avoid invalid intermediate state. Inline struct declarations where possible.
foo, err := newFoo(*fooKey, fooConfig{ Bar: bar, Period: 100 * time.Millisecond, Output: nil, }) if err != nil { log.Fatal(err) } defer foo.close()
*/
//******************************************************************************* // Closures (Function Literals) Functions within Functions + its environment //******************************************************************************* func f() { for i := 0; i < 10; i++ { g := func(i int) { fmt.Printf("%d",i) } g(i) } }
//******************************************************************************* // Arrays // Fixed length sequence of 1 or more elements of a particular type. //******************************************************************************* var ar [3]int ar[0] = 1
// get length len(ar)
func f(a [3]int) { fmt.Println(a) }
f(ar) // passes a copy of the array f(&ar) // passes a pointer to the array
ar[:n] // same as ar[0:n] ar[n:] // same as ar[n:len(ar)] ar[:] // same as [0:len(ar)]
// Literal Array var x [5]int = [5]{1,2,3,4,5}
// print the indices and elements of an array a := [...]int{1,2,3} // in an array literal ellipsis "..." determines len by the num values for i, v := range a { fmt.Printf("%d, %d\n", i, v) }
months := [...]string{1: "January", 2: "February", 3: "March"}
// test for item in array func stringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false }
//******************************************************************************* // Slices // Variable length sequence of 1 or more elements of a particular type. // Window on an underlying array //******************************************************************************* var arr = [...]string{"a", "b", "c", "d", "e"} s1 := arr[1:3] // "b", "c" s2 := arr[2:5] // "c", "d", "e"
fmt.Printf("Length: %d, Capacity: %d", len(s1), cap(s1))
// Slice Literal var sliceLiteral = []int{1,2,3}
// or var sli = make([]int, 10, 15) // array of ints with len of 10 and cap of 15
// Increase size of slice and underlying array if neccessary var sli = make([]int, 0, 3) sli = append(sli, 100) // add the int 100 to append to sli slice
//******************************************************************************* // Maps // Unorder collection of key-value pairs (i.e. hash table or dict) // Keys must be the same type; Values must be the same type // Keys and Values can be of a different type //******************************************************************************* package main
import ( "fmt" )
func main() { // Create a map (hashtable) m := map[string]float64{"one": 1.0, "pi": 3.1415}
// Add a new value
m["two"] = 2.0
// Delete a value
delete(m, "one")
// Detect if a key is in the map
v, p := m["one"] // v = 0, p(resense) = false
printMap(m)
}
func printMap(m map[string]float64) { // Iterate over a map for key, value := range m { fmt.Printf("key %s, value %g\n", key, value) } }
// Test for an entry /* If x is present in the map, set value to the entry for the key and ok to a boolean of true If x is not present in the map, set value to zero of its type (0, "0", nil) and ok to a boolean of false */ value, ok := m[x]
// Delete an entry m[x] = 0, false
elements := map[string]string{ "H": "Hydrogen", "He": "Helium", "Li": "Lithium", "Be": "Beryllium", "B": "Boron", "C": "Carbon", "N": "Nitrogen", "O": "Oxygen", "F": "Fluorine", "Ne": "Neon", }
// Another Example m := make(map[string]string)
m["bandName"] = "Funny Bones" // "create" websiteTitle := m["bandName"] + " Music" // "read" m["bandName"] = "Moon Taxi" // "update" delete(m, "bandName") // "delete" fmt.Printf(m["bandName"]) // prints nothing since m["bandName"] == ""
for key, value := range m { fmt.Println("Key:", key, "Value:", value) }
import "sort"
var m map[int]string
var keys []int
for k := range m {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
fmt.Println("Key:", k, "Value:", m[k])
}
# Defining an interface
type Shape2D interface {
Area() float64
Perimeter() float64
}
# Type Assertion (Switch)
func DrawShape (s Shape2D) bool {
switch := sh s.(type) {
case Rectangle:
DrawRect(sh)
case Triangle:
DrawTriangle(sh)
}
}
github.com/soundcloud/simple/ README.md Makefile main.go main_test.go support.go support_test.go
// Package that needs multiple binaries github.com/soundcloud/complex/ README.md Makefile complex-server/ main.go main_test.go handlers.go handlers_test.go complex-worker/ main.go main_test.go process.go process_test.go shared/ foo.go foo_test.go bar.go bar_test.go
Ideal structure depends on the type of your artifacts. If your repo is exclusively a binary or a library, then make sure consumers can go get or import it by its base path. That is, put the package main or primary importable resource in github.com/name/repo, and use subdirectories for helper packages.
If your repo is a combination of binaries and libraries, then you should determine which is the primary artifact, and put that in the root of the repo. For example, if your repo is primarily a binary, but can also be used as a library, then you’ll want to structure it like this:
github.com/peterbourgon/foo/ main.go // package main main_test.go // package main lib/ foo.go // package foo foo_test.go // package foo
One useful thing is to name the package in the lib/ subdirectory for the library rather than the directory, in this case making it package foo rather than package lib.
If your repo is primarily a library, but it also includes one or more binaries, then you’ll want to structure it like this:
github.com/peterbourgon/foo foo.go // package foo foo_test.go // package foo cmd/ foo/ main.go // package main main_test.go // package main
You’ll invert the structure, putting the library code at the root, and making a cmd/foo/ subdirectory to hold the binary code. The cmd/ intermediary is nice for two reasons: the Go tooling automatically names binaries after the directory of their package main, so it allows us to give them the best possible names without potentially conflicting with other packages in the repo; and it makes it unambiguous to your consumers what they’re getting when they go get a path with /cmd/ in the middle.
import (
"time"
"fmt"
)
fmt.Println("The time is", time.Now())
import "math"
fmt.Println(math.Pi)
func factorial(x uint) uint {
if x == 0 {
return 1
}
return x * factorial(x-1)
}
func data(fileName string) string {
f := os.Open(fileName)
defer f.Close()
contents := io.ReadAll(f)
return contents
}
finfo, err := os.Stat("filename.txt")
if err != nil {
# no such file or dir
return
}
if finfo.IsDir() {
# it's a file
} else {
# it's a directory
}
file, err := ioutil.TempFile(os.TempDir(), "prefix")
defer os.Remove(file.Name())
import "path"
ext := path.Ext(infile)
outfile = infile[0:len(infile)-len(ext)] + ".newExt"
import "fmt"
a := "the"
# Use [1] to use the same value multiple times
fmt.Printf("%s cat in the %[1]s", a)
type Person struct {
name string
addr string
phone string
}
p1 := Person{name:"shaun", addr:"Main St.", phone:"123-456-7890"}
// Marshall - Convert to JSON representation
barr, err := json.Marshal(p1)
if err != nil {
fmt.Println(err)
}
fmt.Println(barr)
# Unmarshalling - Convert to Go object from JSON
var p2 Person
err = json.Unmarshal(barr, &p2)
if err != nil {
fmt.Println(err)
}
import (
"io/ioutil"
)
# Read File
fileData, err := ioutil.ReadFile("test.txt") # not for large files
# Write File
err := ioutil.WriteFile("outfile.txt", fileData, 0777)
# Use os pkg
import (
"os"
)
f, err := os.Open("test.txt")
barr := make([]byte, 10)
nb, err := f.Read(barr) # Control the amount you read
f.Close()
f, err := os.Create("outfile.txt")
barr := []byte{1,2,3}
nb, err := f.Write(barr)
nb, err := f.WriteString("Hi")
f.Close()