initial commit

This commit is contained in:
specCon18 2023-12-18 03:40:05 -05:00
commit 493ce92e02
62 changed files with 1213 additions and 0 deletions

BIN
arrays/arrays Executable file

Binary file not shown.

32
arrays/arrays.go Normal file
View file

@ -0,0 +1,32 @@
package main
import "fmt"
func main(){
//here we create an array a that will hold exactly 5 ints.
// arrays are typed and statically lengthed
// by default an array is zero-valued which for ints is just 0 in all positions
var a [5]int
fmt.Println("emp:",a)
//we can set a value at an index using the arrray[index] = value syntax
//and get the value @ an index with the array[index] syntax
//the builtin len returns the length of an array.
a[4] = 100
fmt.Println("set:", a)
fmt.Println("get:", a[4])
fmt.Println("len:", len(a))
//the following can be used to declare and initalize the valaues
// of an array on a single line
b := [5]int{1,2,3,4,5}
fmt.Println("dcl:", b)
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i+j
}
}
fmt.Println("2d: ", twoD)
}

BIN
channel-buffering/chanbuffer Executable file

Binary file not shown.

View file

@ -0,0 +1,27 @@
package main
import "fmt"
func main(){
//by default channels are unbuffered,
//meaning that they will only accept
//sends(chan <-) if there is a corresponding receive (<- chan) ready
//to receive the sent value. Buffered Channels accept a limited
//number of values without a corresponding receiver for those values
//here we make a channel of strings buffering up to 2 values
messages := make(chan string, 2)
//because this channel is buffered,
//we can send these values into the channel without a
//corresponding concurrent receive
messages <- "buffered"
messages <- "channel"
//later we can receive these two values as usual
fmt.Println(<-messages)
fmt.Println(<-messages)
}

Binary file not shown.

View file

@ -0,0 +1,25 @@
package main
import "fmt"
//this func only accepts a channel for sending values.
//it would be a compile-time error
//to try to receive on this channel.
func ping(pings chan<- string,msg string){
pings <- msg
}
//this pong function accepts one channel for receives (pings)
//and a second for sends(pongs)
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings,pongs)
fmt.Println(<-pongs)
}

BIN
channel-sync/channelsync Executable file

Binary file not shown.

View file

@ -0,0 +1,30 @@
package main
import (
"fmt"
"time"
)
//we can use channels to synchronize execution across goroutines.
//here is an example of using a blocking receive to wait for a goroutine
//to finish whenwaiting for multiple goroutines to finish you may prefer to use a WaitGroup
//This is the function we'll run in a goroutine.
//the done channel will be used to notify another
//goroutine that this function's work is done
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
//start a worker goroutine, giving it the channel to notify on.
done <- true
}
func main(){
done := make(chan bool,1)
go worker(done)
//block until we receive a notification from the worker on the channel
<-done
}

BIN
channels/channels Executable file

Binary file not shown.

22
channels/channels.go Normal file
View file

@ -0,0 +1,22 @@
package main
import "fmt"
//Channels are the pipes that connect concurrent goroutines.
//you can send values into channels
//form one goroutine and receive those values into another goroutine
func main(){
//create a new channel with make(chan val-type)
//Channels are typed bt the values they convey
messages := make(chan string)
//send a value into a channel using the channel <- syntax.
//here we send "ping" to the messages channel we made above, from a new goroutine
go func() { messages <- "ping"}()
//the <- channel syntax receives a value from the channel.
// here we'll receive the "ping" message we sent above and print it out
msg := <- messages
fmt.Println(msg)
}

BIN
closures/closures Executable file

Binary file not shown.

30
closures/closures.go Normal file
View file

@ -0,0 +1,30 @@
package main
import "fmt"
// This function returns another function,
// which we define anonymously in the body of the intSeq.
// The returned function closes over the variable i to form a closure
func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}
func main(){
//we call intSeq assigning the result(a function) to nextInt.
// This function value captures its own i value, which will be updated
// each time we call nextInt
nextInt := intSeq()
// see the effect of the closure by calling nextInt a few times.
fmt.Println(nextInt())
fmt.Println(nextInt())
fmt.Println(nextInt())
// to confirm that the state is unique to the instance create a new instance
newInts := intSeq()
fmt.Println(newInts())
}

BIN
constants/constants Executable file

Binary file not shown.

24
constants/constants.go Normal file
View file

@ -0,0 +1,24 @@
package main
import (
"fmt"
"math"
)
//const declares a constant value.
const s string = "constant"
func main(){
fmt.Println(s)
// A const statement can appear anywhere a var statement can.
const n = 500000000
// Constant expressions perform arithmetic with arbitrary precision.
const d = 3e20 / n
// A numeric constant has no type until its given one, such as by an explicit conversion.
fmt.Println(int64(d))
// A number can be given a type by using it
// in a context that requires one, such
// as a variable assignment or function call.
// For example, here math.Sin expects a float64.
fmt.Println(math.Sin(n))
}

BIN
errors/errors Executable file

Binary file not shown.

68
errors/errors.go Normal file
View file

@ -0,0 +1,68 @@
package main
import (
"errors"
"fmt"
)
//In Go its idiomatic to communicate errors via an explicit, seperate return value.
//this contrasts with the exceptions used in languages like java and Ruby
//and the verloaded single result/error value sometimes used in C.
//Go's approach makes it easy to see which functions return errors and to
//handle them using the same language constructs employed for any other non-error tasks
// by convention, errors are the last return value and have type error, a builtin interface
func f1(arg int) (int,error){
if arg == 42 {
//errors.New constructs a basic error value with the given error message
return -1, errors.New("cant work with 42")
}
//a nil value in the error position indicates that there was no error
return arg + 3, nil
}
// its possible to use custom types as errors by implementing Error() method on them.
// Heres a variant on the example above that uses a custom error type
type argError struct {
arg int
prob string
}
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int,error) {
if arg == 42 {
return -1, &argError{arg, "can't work with it"}
}
return arg+3,nil
}
func main(){
//The two loops below test out each of our error-returning
//functions. Note that the use of an inline error check on the if line is a common idiom in Go code.
for _,i := range []int{7,42} {
if r,e :=f1(i); e != nil {
fmt.Println("f1 failed:",e)
} else {
fmt.Println("f1 worked:",r)
}
}
for _,i := range []int{7,42} {
if r,e := f2(i);e != nil {
fmt.Println("f2 failed:",e)
} else {
fmt.Println("f2 worked:",r)
}
}
//if you want to programmatically use the data in a custom error, you'll need to get the error
// as an instance of the custom error type via type assertion
_, e := f2(42)
if ae, ok:= e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}

61
flake.lock generated Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1702312524,
"narHash": "sha256-gkZJRDBUCpTPBvQk25G0B7vfbpEYM5s5OZqghkjZsnE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a9bf124c46ef298113270b1f84a164865987a91c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

18
flake.nix Normal file
View file

@ -0,0 +1,18 @@
{
description = "Go development environment";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs = { self, flake-utils, nixpkgs }:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system}; in
{
devShells.default = with pkgs; mkShell {
packages = [
go
];
shellHook = ''
'';
};
}
);
}

32
for/for.go Normal file
View file

@ -0,0 +1,32 @@
package main
import "fmt"
func main(){
i := 1
// for with a single condition
for i <= 3 {
fmt.Println(i)
i = i+1
}
// Initial/Condition/After classic style loop
for j := 7; j <= 9; j++ {
fmt.Println(j)
}
// due to go not having any other loop for with no condition == while
for {
fmt.Println("loop")
break
}
//continue passes to the next iter of the loop
for n := 0; n <= 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
}

BIN
functions/functions Executable file

Binary file not shown.

25
functions/functions.go Normal file
View file

@ -0,0 +1,25 @@
package main
import "fmt"
// here is a function that takes two ints and sums them
// then returns them as an int
// go requires explicit returns meaning it wont
// automatically return the value of the last expression
func plus(a int,b int) int {
return a+b
}
//when you have multiple consecutive params with the same type they can be type declared as such:
func plusPlus(a,b,c int) int {
return a+b+c
}
//call functions like you would expect
func main(){
res := plus(1,2)
fmt.Println("1+2=",res)
res = plusPlus(1,2,3)
fmt.Println("1+2+3=",res)
}

BIN
generics/generics Executable file

Binary file not shown.

66
generics/generics.go Normal file
View file

@ -0,0 +1,66 @@
package main
import "fmt"
//as an example of a generic function,
//MapKeys takes a map of any type and returns a slice of its keys.
//this function has two type parameters - K and V; K has the comparable constraint
//meaning that we can compare values of this type with the == and != operators
//this is required for map keys in Go V has the any constraint meaning that its
// not restricted in any way(any is an alias for interface{})
func MapKeys[K comparable, V any](m map [K]V) []K {
r := make([]K,0,len(m))
for k := range m {
r = append(r,k)
}
return r
}
//as example of a generic type, List is a singly-linked list with values of any type
type List[T any] struct {
head, tail *element[T]
}
type element[T any] struct {
next *element[T]
val T
}
//We can define methods on generic types just like we do on regular types,
// but we have to keep the type parameters in place.
// the type is List[T] not List
func (lst *List[T]) Push(v T) {
if lst.tail == nil {
lst.head = &element[T]{val:v}
lst.tail = lst.head
} else {
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}
func (lst *List[T]) GetAll() []T {
var elems []T
for e := lst.head; e != nil; e = e.next {
elems = append(elems, e.val)
}
return elems
}
func main() {
var m = map[int]string{1: "2",2:"4",4:"8"}
//When invoking genric functions, we can often
// rely on type inference. note that we dont have to specify
// the types for K and V when calling MapKeys - the compiler infers them automatically
fmt.Println("keys:",MapKeys(m))
//though we could be explicit
_ = MapKeys[int,string](m)
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)
fmt.Println("list:",lst.GetAll())
}

BIN
goroutines/goroutines Executable file

Binary file not shown.

33
goroutines/goroutines.go Normal file
View file

@ -0,0 +1,33 @@
package main
import (
"fmt"
"time"
)
func f (from string){
for i := 0; i<3; i++ {
fmt.Println(from,":",i)
}
}
func main(){
//suppose we have function call f(s)
//here is how wed call that in the usual way
//running it synchronously
f("direct")
//To invoke this function in a goroutine, use go f(s)
//This new goroutine will execute concurrently with the calling one
go f("goroutine")
//you can also start a goroutine for an anonymous function call
go func (msg string) {
fmt.Println(msg)
}("going")
//our two function calls are running async in seperate goroutines now.
//wait for them to finish (for a most robust approach use a WaitGroup)
time.Sleep(time.Second)
fmt.Println("done")
}

BIN
hello-world/hello-world Executable file

Binary file not shown.

View file

@ -0,0 +1,5 @@
package main
import "fmt"
func main(){
fmt.Println("hello world")
}

BIN
if-else/if-else Executable file

Binary file not shown.

35
if-else/if-else.go Normal file
View file

@ -0,0 +1,35 @@
package main
import "fmt"
func main(){
//There are no ternary ifs in go
//basic if else
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
//if doesnt need else
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
//You can use logical operators in if such as && and ||
if 7%2 == 0 || 8%2 == 0 {
fmt.Println("either 8 or 7 are even")
}
//a statement can precede conditionals;
// any variables declared in the statement
//are avalible in the current and all subsequent branches.
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}

BIN
interfaces/interfaces Executable file

Binary file not shown.

54
interfaces/interfaces.go Normal file
View file

@ -0,0 +1,54 @@
package main
import (
"fmt"
"math"
)
// here is a basic interface for geometric shapes
type geometry interface {
area() float64
perim() float64
}
//for our example well implement this interface on rect and circle types
type rect struct {
width,height float64
}
type circle struct {
radius float64
}
// to implement an interface in Go,
//we just need to implement all the methods in the interface
//here we do it for rectangles
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perim() float64 {
return 2*r.width + 2*r.height
}
//here we do it for circles
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2*math.Pi*c.radius
}
//if a variable has an interface type,
// then we can call methods that are in the named interface
//here's a generic measure function taking advantage of this
//to work on any geometry
func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
func main(){
r := rect{width: 3,height: 4}
c := circle{radius:5}
measure(r)
measure(c)
}

BIN
maps/maps Executable file

Binary file not shown.

57
maps/maps.go Normal file
View file

@ -0,0 +1,57 @@
package main
import (
"fmt"
"maps"
)
func main() {
// to create an empty map, use the builtin make:
// make(map[key-type]val-type)
m := make(map[string]int)
//set key/value pairs using typical name[key] = val syntax
m["k1"] = 7
m["k2"] = 13
// printing a map with e.g.
// fmt.Println will show all of its key/value pairs
fmt.Println("map:",m)
// Get a value from a key with name[key]
// if the key doesn't exist the zero value of the value type is returned
v1 := m["k1"]
fmt.Println("v1:",v1)
v3 := m["k3"]
fmt.Println("v3:",v3)
//the builtin len returns the number of key/value pairs when called on a map
fmt.Println("len:", len(m))
//the builtin delete removes key/value pairs from a map,
//use the clear builtin to remove all key/value pairs from a map.
delete(m, "k2")
fmt.Println("map:",m)
clear(m)
fmt.Println("map:",m)
// the optional second return value when getting the value
// from a map indicates if the key was present in the map allowing
// to diferentiate between default zero-values and values acutally stored as 0
// here we dont need the value itself so we ignore it with the _ or blank identifier
_, prs := m["k2"]
fmt.Println("prs:",prs)
//you can declare and initalize a new map in the same line with the following syntax:
n := map[string]int{"foo": 1,"bar": 2}
fmt.Println("map:", n)
//the map package also contains useful funcs for map such as .Equal
n2 := map[string]int{"foo": 1,"bar":2}
if maps.Equal(n,n2){
fmt.Println("n == n2")
}
}

BIN
methods/methods Executable file

Binary file not shown.

34
methods/methods.go Normal file
View file

@ -0,0 +1,34 @@
package main
import "fmt"
type rect struct {
width, height int
}
//this area method has a reciever type of *rect
func (r *rect) area() int {
return r.width * r.height
}
// Methods can be defined for either pointer or value reciever types
// heres an example of a value receiver
func (r rect) perim() int {
return 2*r.width + 2*r.height
}
func main() {
r := rect{width:10,height:5}
//here we call the two methods defined for our struct
fmt.Println("area:",r.area())
fmt.Println("perim:",r.perim())
//Go automatically handles conversion between values and pointers for method calls.
//you may want to use a pointer receiver type to avoid copying on method calls or to allow
//the method to mutate the receiving struct
rp := &r
fmt.Println("area:",rp.area())
fmt.Println("perim:",rp.perim())
}

BIN
multi-return-values/mrv Executable file

Binary file not shown.

View file

@ -0,0 +1,18 @@
package main
import "fmt"
//the (int,int) in the func sig shows that the func returns 2 ints
func vals()(int,int){
return 3,7
}
func main(){
//here we use the 2 different values from the call with multiple assignment
a,b := vals()
fmt.Println(a)
fmt.Println(b)
//if you only want a subset of the returned values use the blank identifier
_,c := vals()
fmt.Println(c)
}

BIN
pointers/pointers Executable file

Binary file not shown.

37
pointers/pointers.go Normal file
View file

@ -0,0 +1,37 @@
package main
import "fmt"
// go supports pointers allowing you to pass refs to values and records
// in your program
//this func takes in an int as a param
func zeroval(ival int){
ival = 0
}
//this func takes in an pointer to an int as a parameter
func zeroptr (iptr *int) {
*iptr = 0
}
func main() {
//display initial value of i
i := 1
fmt.Println("initial:",i)
//display the value of i after passing to zeroval
zeroval(i)
fmt.Println("zeroval:",i)
//display the value of the data @ the memory address of i aka i's pointer
zeroptr(&i)
fmt.Println("zeroptr:",i)
//pointers can be printed as well and will return their memory address
fmt.Println("pointer:",&i)
//zeroval doesnt change the val of i but zeroptr does because it edits the data at the memory address
}

BIN
range/range Executable file

Binary file not shown.

37
range/range.go Normal file
View file

@ -0,0 +1,37 @@
package main
import "fmt"
func main(){
//here we use range to sum the numbers in a slice
nums := []int{2,3,4}
sum := 0
for _,num := range nums {
sum += num
}
fmt.Println("sum:",sum)
//range on arrays and slices provides both the index and value
// at each entry. above we didn't need the index, so we ignored it with the blank identifier
// but if we want the index we can use it like bellow
for i, num := range nums {
if num == 3 {
fmt.Println("index:",i)
}
}
//range on map iterates over the key value pairs.
kvs := map[string]string{"a":"apple","b":"banana"}
for k,v := range kvs{
fmt.Printf("%s -> %s\n",k,v)
}
//range can also iterate over just the keys of a map
for k := range kvs{
fmt.Println("key:",k)
}
// range on strings iterates over Unicode code points.
// the first value is the starting byte index of the rune
//and the second is the rune itself.
for i,c := range "go"{
fmt.Println(i,c)
}
}

BIN
recursion/recursion Executable file

Binary file not shown.

30
recursion/recursion.go Normal file
View file

@ -0,0 +1,30 @@
package main
import "fmt"
//this calls itself until it reaches the base case of fact(0)
func fact(n int) int {
if n == 0 {
return 1
}
return n * fact(n-1)
}
func main() {
fmt.Println(fact(7))
// closures can also be recursive,
// but this requires the closure to be declared with
// a typed var explicitly before its defined
var fib func(n int) int
fib = func(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
//simce fib was previously declared in main Go knows which function to call with fib here
fmt.Println(fib(7))
}

BIN
select/select Executable file

Binary file not shown.

39
select/select.go Normal file
View file

@ -0,0 +1,39 @@
package main
import (
"fmt"
"time"
)
//Go's select lets you wait on multiple channel
//operations combining goroutines and channels with select
//is a powerful feature of go
func main(){
//For our example well select across two channels.
c1 := make(chan string)
c2 := make(chan string)
//each channel will receive a value after some
//amount of time, to simulate e.g. blocking RPC
//operations executing in concurrent goroutines
go func(){
time.Sleep(1*time.Second)
c1 <- "one"
}()
go func(){
time.Sleep(2*time.Second)
c2 <- "two"
}()
//we'll use select to await both of these values
//simultaneously, printing each one as it arrives.
for i := 0; i < 2; i++ {
select{
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}

BIN
slices/slices Executable file

Binary file not shown.

76
slices/slices.go Normal file
View file

@ -0,0 +1,76 @@
package main
import (
"fmt"
"slices"
)
func main(){
// Unlike arrays, slices are typed only by the elements they contain.
// not the number of elements.
// an uninitalized slice equals to nil and has a len of 0
var s []string
fmt.Println("uninit:", s, s == nil, len(s) == 0)
// to create an emply slice with non-zero length,
// use the builtin make. here we make a slice of strings of length 3 (initally zero-valued)
// by default a new slice's capacity == its len if we know the slice is going to grow ahead of time
// its possible to pass a capacity explicitly as an additional parameter to make.
s = make([]string, 3)
fmt.Println("emp:", s, len(s), "cap:", cap(s))
//setting and getting vars works just like arrays
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("set:", s)
fmt.Println("get:", s[2])
// len reutrns the length of the slice as expected.
fmt.Println("len:", len(s))
// in addition to these basic opertaions,
// slices support several more than make them richer than arrays.
// One is the builtin append which returns a slice containing
// one or more new values. Note that we need to accept a return value
//from append as we make get a new slice value.
s = append(s, "")
s = append(s, "e", "f")
// slices can be copy'd
// below we create an empty slice c
// of the same len as s and copy into c from s
c := make([]string, len(s))
copy(c,s)
fmt.Println("cpy:",c)
//slices also support a slice operator with the syntax slice[low:high].
// for example this gets a slice of the elements s[2],s[3],s[4]
l := s[2:5]
fmt.Println("sl1", l)
l = s[:5]
fmt.Println("sl2", l)
l = s[2:]
fmt.Println("sl3", l)
//single line declaration and initalization of a slice
t := []string{"g","h","i"}
//the slices package has plenty of useful funcs for slices like .Equal
t2 := []string{"g","h","i"}
if slices.Equal(t,t2){
fmt.Println("t == t2")
}
// Slices can be composed into multi-dimensional data structues,
// the length of the inner slices can vary, unlike with multi-dimensional arrays.
twoD := make([][]int, 3)
for i := 0; i < 3; i ++ {
innerLen := i + 1
twoD[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}

BIN
strings-and-runes/stringsandrunes Executable file

Binary file not shown.

View file

@ -0,0 +1,55 @@
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
// s is a string assigned a literal value representing the
// word "hello" in the Thai language.
// Go string literals are UTF-8 encoded text.
const s = "สวัสดี"
//Since strings are equivalent to []byte,
//this will produce the length of the raw bytes stored within
fmt.Println("len:", len(s))
// indexing into a string produces the raw byte values at each index.
// This loop generates the hex values of all the bytes
// that constitute the code points in s.
for i := 0; i < len(s); i++ {
fmt.Printf("%x ", s[i])
}
fmt.Println()
// To count how many runes are in a string,
// we ca can use the utf8 package. Note that
// the run-time of RuneCountInString depends on the size of the string
// due to having to decode each rune sequentially.
// Some Thai characters are represented by multiple UTF-8 code points,
// so the result of this count may be suprising.
fmt.Println("Rune count:",utf8.RuneCountInString(s))
// A range loop handles strings specially and decodes each rune
// along with it's offset in the string
for idx, runeValue := range s {
fmt.Printf("%#U Starts at %d\n", runeValue, idx)
}
//we can achieve the same iteration by using the utf8.DecodeRuneInString function explicitly
fmt.Println("\nUsing DecodeRuneInString")
for i,w := 0,0; i < len(s); i += w {
runeValue, width := utf8.DecodeRuneInString(s[i:])
fmt.Printf("%#U starts at %d\n", runeValue, i)
w = width
// This demonstrates passing a rune to a func
examineRune(runeValue)
}
}
// Values enclosed in single quotes are rune literals.
// we can compare a rune value to a rune literal directly.
func examineRune(r rune) {
if r == 't' {
fmt.Println("found tee")
} else if r == 'ส' {
fmt.Println("found so sua")
}
}

BIN
struct-embedding/structembed Executable file

Binary file not shown.

View file

@ -0,0 +1,54 @@
package main
import "fmt"
//Go struct embedding allows for structs to be embedded into interfaces
// to express a more seamless composition of types.
type base struct {
num int
}
func (b base) describe() string{
return fmt.Sprintf("base with num=%v",b.num)
}
//a container embeds a base
//an embedding looks like a field without a name
type container struct {
base
str string
}
func main(){
//when creating structs with literals, we have to
//initalize the embedding explicitly;
// here the embedded types serve as the field name
co := container{
base: base {
num: 1,
},
str: "some name",
}
//we can access the base's fields directly on co, e.g. co.num
fmt.Printf("co={num: %v str: %v}\n", co.num, co.str)
//alternativly we can spell out the full path using the embedded type name
fmt.Println("also num:", co.base.num)
//since container embeds base, the methods of base also become the methods of container
//here we invoke a method that was embedded from base directly on co
fmt.Println("describe:", co.describe())
//Embedding structs with methods may be used
// to bestow interface implementations onto other structs.
// here we see that container now implements the describer interface because it
// embeds base
type describer interface {
describe() string
}
var d describer = co
fmt.Println("describer:", d.describe())
}

BIN
structs/structs Executable file

Binary file not shown.

58
structs/structs.go Normal file
View file

@ -0,0 +1,58 @@
package main
import "fmt"
// Structs are typed collections of fields.
// they're useful for grouping data together to form records
// the person struct type has name and age fields
type person struct {
name string
age int
}
// newPerson constructs a new person struct with the given name
// you can safely return a pointer to local variable as a local variable
// will survive the scope of the func
func newPerson(name string) *person {
p := person{name: name}
p.age = 42
return &p
}
func main(){
// This syntax creates a new struct
fmt.Println(person{"Bob", 20})
//you can name the fields when initalizing the struct
fmt.Println(person{name: "Alice", age: 30})
//omitted fields will be zero-valued
fmt.Println(person{name:"Fred"})
//an & prefix yields a pointer to the struct
fmt.Println(&person{name:"Ann",age: 40})
//its idiomatic to encapsulate new struct creation in constructor functions
fmt.Println(newPerson("Jon"))
//access struct fields with . notation
s := person{name: "Sean", age: 50}
fmt.Println(s.name)
// you can also use . notation with struct pointers - the pointers are
// automatically dereferenced
sp := &s
fmt.Println(sp.age)
// structs are mutable
sp.age = 51
fmt.Println(sp.age)
// if a struct type is only used for a single value
// we dont have to give it a name
// the value can have an anonymous struct type.
// this tech is used commonly in table driven tests
dog := struct {
name string
isGood bool
}{
"Rex",
true,
}
fmt.Println(dog)
}

BIN
switch/switch Executable file

Binary file not shown.

54
switch/switch.go Normal file
View file

@ -0,0 +1,54 @@
package main
import (
"fmt"
"time"
)
func main() {
// Basic switch default is optional
i := 2
fmt.Print("Write", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}
// Commas can seperate multiple expressions in the same case statement.
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("It's the weekend")
default:
fmt.Println("It's a weekday")
}
//switch without an expression is a alternate way to express if/else logic
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's afte noon")
}
// a type switch compares types instead of values.
//you can use this to discorver the type of an interface value.
whatAmI := func(i interface{}){
switch t := i.(type) {
case bool:
fmt.Println("I'm a bool")
case int:
fmt.Println("I'm an int")
default:
fmt.Printf("Don't know the type %T\n",t)
}
}
whatAmI(true)
whatAmI(1)
whatAmI("hey")
}

10
timeouts/timeouts.go Normal file
View file

@ -0,0 +1,10 @@
package main
import (
"fmt"
"time"
)
func main() {
}

BIN
values/values Executable file

Binary file not shown.

20
values/values.go Normal file
View file

@ -0,0 +1,20 @@
package main
import "fmt"
func main(){
// Strings which can be concatinated by +
fmt.Println("go"+"lang")
// Strings can be iterpolated with a result
// of an expression using fmt.Println and
// seperating the expression and string by a ,
// in this case the string is interpolated with
// a sum of ints and sum of floats
fmt.Println("1+1=",1+1)
fmt.Println("7.0/3.0=",7.0/3.0)
// Booleans and boolean operators
fmt.Println(true && false)
fmt.Println(true || false)
fmt.Println(!true)
}

BIN
variables/variables Executable file

Binary file not shown.

27
variables/variables.go Normal file
View file

@ -0,0 +1,27 @@
package main
import "fmt"
func main(){
// var declares 1 or more variables.
var a = "initial"
fmt.Println(a)
// You can declare multiple variables at once.
var b,c int = 1,2
fmt.Println(b,c)
// Go will infer the type of initialized variables.
var d = true
fmt.Println(d)
// Variables declared without a corresponding
// initialization are zero-valued. For example,
// the zero value for an int is 0.
var e int
fmt.Println(e)
// The := syntax is shorthand for declaring and
// initializing a variable, e.g.
// for var f string = "apple" in this case.
// This syntax is only available inside functions.
f := "apple"
fmt.Println(f)
}

BIN
variadic-functions/vfunc Executable file

Binary file not shown.

View file

@ -0,0 +1,20 @@
package main
import "fmt"
// below is a func that takes in an arbitrary number of ints as args
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func main(){
sum(1,2)
sum(1,2,3)
nums := []int{1,2,3,4}
sum(nums...)
}