├── README.md ├── app ├── data.txt ├── go.mod ├── main.go └── mypackage │ └── mypackage.go ├── app2 ├── go.mod ├── testemail.go └── testemail_test.go ├── cltest.go ├── data.txt ├── hellogo.go └── webapp ├── new.html ├── todos.txt ├── view.html └── webapp.go /README.md: -------------------------------------------------------------------------------- 1 | # Go-Tutorial 2 | I created this Golang tutorial to be the definitive course. I provide in this full course more information than you can get in any book. The table of contents follows below. Both the core language and numerous projects are provided to help you master Go. Also there is only one 5 second ad, so nothing will get in the way of your learning process! Go is one of the fastest growing languages and for good reason. Go makes it very easy to write fast code without having to tweak with the code. The code is understandable, efficient, excels at concurrency, provides great backend support for web apps, handles errors much like Rust and handles memory efficiently. 3 | -------------------------------------------------------------------------------- /app/data.txt: -------------------------------------------------------------------------------- 1 | 2 2 | 3 3 | 5 4 | 7 5 | 11 6 | 13 7 | -------------------------------------------------------------------------------- /app/go.mod: -------------------------------------------------------------------------------- 1 | module example/project 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /app/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // The path to your project is declared in your 4 | // go.mod file followed by the directory 5 | import ( 6 | stuff "example/project/mypackage" 7 | "fmt" 8 | "log" 9 | "reflect" 10 | ) 11 | 12 | func main() { 13 | fmt.Println("Hello", stuff.Name) 14 | intArr := []int{2, 3, 5, 7, 11} 15 | strArr := stuff.IntArrToStrArr(intArr) 16 | fmt.Println(strArr) 17 | fmt.Println(reflect.TypeOf(strArr)) 18 | 19 | // Demonstrating encapsulation 20 | date := stuff.Date{} 21 | err := date.SetMonth(12) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | err = date.SetDay(21) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | err = date.SetYear(1974) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | fmt.Printf("1st Day : %d/%d/%d\n", 34 | date.Month(), date.Day(), date.Year()) 35 | } 36 | -------------------------------------------------------------------------------- /app/mypackage/mypackage.go: -------------------------------------------------------------------------------- 1 | package stuff 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | var Name string = "Derek" 10 | 11 | // Function name is uppercase so it can be exported 12 | func IntArrToStrArr(intArr []int) []string { 13 | var strArr []string 14 | for _, i := range intArr { 15 | strArr = append(strArr, strconv.Itoa(i)) 16 | } 17 | return strArr 18 | } 19 | 20 | // We capitalize what we want to export and use 21 | // lowercase on what we don't. We don't want the 22 | // user to be able to access day, month, year 23 | // directly 24 | type Date struct { 25 | day int 26 | month int 27 | year int 28 | } 29 | 30 | // Create a setter function for the values 31 | // Make sure all values are valid or return 32 | // an error message 33 | func (d *Date) SetDay(day int) error { 34 | if (day < 1) || (day > 31) { 35 | return errors.New("incorrect day value") 36 | } 37 | d.day = day 38 | return nil 39 | } 40 | func (d *Date) SetMonth(m int) error { 41 | if (m < 1) || (m > 12) { 42 | return errors.New("incorrect month value") 43 | } 44 | d.month = m 45 | return nil 46 | } 47 | func (d *Date) SetYear(y int) error { 48 | if (y < 1875) || (y > time.Now().Year()) { 49 | return errors.New("incorrect year value") 50 | } 51 | d.year = y 52 | return nil 53 | } 54 | 55 | // Getter functions return the values 56 | func (d *Date) Day() int { 57 | return d.day 58 | } 59 | func (d *Date) Month() int { 60 | return d.month 61 | } 62 | func (d *Date) Year() int { 63 | return d.year 64 | } 65 | -------------------------------------------------------------------------------- /app2/go.mod: -------------------------------------------------------------------------------- 1 | module app2 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /app2/testemail.go: -------------------------------------------------------------------------------- 1 | package app2 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | ) 7 | 8 | func IsEmail(s string) (string, error) { 9 | // Used a raw string here so I didn't have 10 | // to double backslashes 11 | r, _ := regexp.Compile(`[\w._%+-]{1,20}@[\w.-]{2,20}.[A-Za-z]{2,3}`) 12 | 13 | if r.MatchString(s) { 14 | return "Valid Email", nil 15 | } else { 16 | return "", fmt.Errorf("not a valid email") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app2/testemail_test.go: -------------------------------------------------------------------------------- 1 | package app2 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIsEmail(t *testing.T) { 8 | _, err := IsEmail("hello") 9 | if err == nil { 10 | t.Error("hello is not an email") 11 | } 12 | 13 | _, err = IsEmail("derek@aol.com") 14 | if err != nil { 15 | t.Error("derek@aol.com is an email") 16 | } 17 | 18 | _, err = IsEmail("derek@aol") 19 | if err != nil { 20 | t.Error("derek@aol is not email") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cltest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func main() { 10 | fmt.Println(os.Args) 11 | // Get all values after the first index 12 | args := os.Args[1:] 13 | 14 | // Create int array from string array 15 | var iArgs = []int{} 16 | for _, i := range args { 17 | val, err := strconv.Atoi(i) 18 | if err != nil { 19 | panic(err) 20 | } 21 | iArgs = append(iArgs, val) 22 | } 23 | 24 | max := 0 25 | for _, val := range iArgs { 26 | if val > max { 27 | max = val 28 | } 29 | } 30 | fmt.Println("Max Value :", max) 31 | } 32 | -------------------------------------------------------------------------------- /data.txt: -------------------------------------------------------------------------------- 1 | 2 2 | 3 3 | 5 4 | 7 5 | 11 6 | 13 7 | -------------------------------------------------------------------------------- /hellogo.go: -------------------------------------------------------------------------------- 1 | // A package is a collection of code 2 | // We can define what package we want our code to belong to 3 | // We use main when we want our code to run in the terminal 4 | package main 5 | 6 | // Import multiple packages 7 | // You could use an alias like f "fmt" 8 | import ( 9 | "bufio" 10 | "errors" 11 | "fmt" 12 | "log" 13 | "math" 14 | "math/rand" 15 | "os" 16 | "reflect" 17 | "regexp" 18 | "strconv" 19 | "strings" 20 | "sync" 21 | "time" 22 | "unicode/utf8" 23 | ) 24 | 25 | // Create alias to long function names 26 | var pl = fmt.Println 27 | 28 | /* 29 | I'm a block comment 30 | */ 31 | 32 | // ----- FUNCTIONS ----- 33 | func sayHello() { 34 | pl("Hello") 35 | } 36 | 37 | // Returns sum of values 38 | func getSum(x int, y int) int { 39 | return x + y 40 | } 41 | 42 | // Return multiple values 43 | func getTwo(x int) (int, int) { 44 | return x + 1, x + 2 45 | } 46 | 47 | // Return potential error 48 | func getQuotient(x float64, y float64) (ans float64, err error) { 49 | if y == 0 { 50 | // Define error message returned with dummy value 51 | // for ans 52 | return 0, fmt.Errorf("You can't divide by zero") 53 | } else { 54 | // If no error return nil 55 | return x / y, nil 56 | } 57 | } 58 | 59 | // Variadic function 60 | func getSum2(nums ...int) int { 61 | sum := 0 62 | // nums gets converted into a slice which is 63 | // iterated by range (More on slices later) 64 | for _, num := range nums { 65 | sum += num 66 | } 67 | return sum 68 | } 69 | 70 | func getArraySum(arr []int) int { 71 | sum := 0 72 | for _, val := range arr { 73 | sum += val 74 | } 75 | return sum 76 | } 77 | 78 | func changeVal(f3 int) int { 79 | f3 += 1 80 | return f3 81 | } 82 | 83 | func changeVal2(myPtr *int) { 84 | *myPtr = 12 85 | } 86 | 87 | // Receives array by reference and doubles values 88 | func dblArrVals(arr *[4]int) { 89 | for x := 0; x < 4; x++ { 90 | arr[x] *= 2 91 | } 92 | } 93 | 94 | func getAverage(nums ...float64) float64 { 95 | var sum float64 = 0.0 96 | var numSize float64 = float64(len(nums)) 97 | 98 | for _, val := range nums { 99 | sum += val 100 | } 101 | return (sum / numSize) 102 | } 103 | 104 | // ----- FUNCTION THAT EXCEPTS GENERICS ----- 105 | // This generic type parameter is capital, between 106 | // square brackets and has a rule for what data 107 | // it will except called a constraint 108 | // any : anything 109 | // comparable : Anything that supports == 110 | // More Constraints : pkg.go.dev/golang.org/x/exp/constraints 111 | 112 | // You can also define what is excepted like this 113 | // Define that my generic must be an int or float64 114 | type MyConstraint interface { 115 | int | float64 116 | } 117 | 118 | func getSumGen[T MyConstraint](x T, y T) T { 119 | return x + y 120 | } 121 | 122 | // ----- STRUCTS ----- 123 | type customer struct { 124 | name string 125 | address string 126 | bal float64 127 | } 128 | 129 | // This struct has a function associated 130 | type rectangle struct { 131 | length, height float64 132 | } 133 | 134 | func (r rectangle) Area() float64 { 135 | return r.length * r.height 136 | } 137 | 138 | // Customer passed as values 139 | func getCustInfo(c customer) { 140 | fmt.Printf("%s owes us %.2f\n", c.name, c.bal) 141 | } 142 | 143 | func newCustAdd(c *customer, address string) { 144 | c.address = address 145 | } 146 | 147 | // Struct composition : Putting a struct in another 148 | type contact struct { 149 | fName string 150 | lName string 151 | phone string 152 | } 153 | 154 | type business struct { 155 | name string 156 | address string 157 | contact 158 | } 159 | 160 | func (b business) info() { 161 | fmt.Printf("Contact at %s is %s %s\n", b.name, b.contact.fName, b.contact.lName) 162 | } 163 | 164 | // ----- DEFINED TYPES ----- 165 | // I'll define different cooking measurement types 166 | // so we can do conversions 167 | type Tsp float64 168 | type TBs float64 169 | type ML float64 170 | 171 | // Convert with functions (Bad Way) 172 | func tspToML(tsp Tsp) ML { 173 | return ML(tsp * 4.92) 174 | } 175 | 176 | func TBToML(tbs TBs) ML { 177 | return ML(tbs * 14.79) 178 | } 179 | 180 | // Associate method with types 181 | func (tsp Tsp) ToMLs() ML { 182 | return ML(tsp * 4.92) 183 | } 184 | func (tbs TBs) ToMLs() ML { 185 | return ML(tbs * 14.79) 186 | } 187 | 188 | // ----- INTERFACES ----- 189 | type Animal interface { 190 | AngrySound() 191 | HappySound() 192 | } 193 | 194 | // Define type with interface methods and its 195 | // own method 196 | type Cat string 197 | 198 | func (c Cat) Attack() { 199 | pl("Cat Attacks its Prey") 200 | } 201 | 202 | // Return the cats name with a type conversion 203 | func (c Cat) Name() string { 204 | return string(c) 205 | } 206 | 207 | func (c Cat) AngrySound() { 208 | pl("Cat says Hissssss") 209 | } 210 | func (c Cat) HappySound() { 211 | pl("Cat says Purrr") 212 | } 213 | 214 | // ----- CONCURRENCY ----- 215 | func printTo15() { 216 | for i := 1; i <= 15; i++ { 217 | pl("Func 1 :", i) 218 | } 219 | } 220 | func printTo10() { 221 | for i := 1; i <= 10; i++ { 222 | pl("Func 2 :", i) 223 | } 224 | } 225 | 226 | // These functions will print in order using 227 | // channels 228 | // Func receives a channel and then sends values 229 | // over channels once each time it is called 230 | func nums1(channel chan int) { 231 | channel <- 1 232 | channel <- 2 233 | channel <- 3 234 | } 235 | func nums2(channel chan int) { 236 | channel <- 4 237 | channel <- 5 238 | channel <- 6 239 | } 240 | 241 | // ----- BANK ACCOUNT EXAMPLE ----- 242 | // Here I'll simulate customers accessing a 243 | // bank account and lock out customers to 244 | // allow for individual access 245 | type Account struct { 246 | balance int 247 | lock sync.Mutex // Mutual exclusion 248 | } 249 | 250 | func (a *Account) GetBalance() int { 251 | a.lock.Lock() 252 | defer a.lock.Unlock() 253 | return a.balance 254 | } 255 | 256 | func (a *Account) Withdraw(v int) { 257 | a.lock.Lock() 258 | defer a.lock.Unlock() 259 | if v > a.balance { 260 | pl("Not enough money in account") 261 | } else { 262 | fmt.Printf("%d withdrawn : Balance : %d\n", 263 | v, a.balance) 264 | a.balance -= v 265 | } 266 | } 267 | 268 | // ----- CLOSURES ----- 269 | // Pass a function to a function 270 | func useFunc(f func(int, int) int, x, y int) { 271 | pl("Answer :", (f(x, y))) 272 | } 273 | 274 | func sumVals(x, y int) int { 275 | return x + y 276 | } 277 | 278 | // ----- RECURSION ----- 279 | func factorial(num int) int { 280 | // This condition ends calling functions 281 | if num == 0 { 282 | return 1 283 | } 284 | return num * factorial(num-1) 285 | } 286 | 287 | // When a Go program executes it executes a function named main 288 | // Go statements don't require semicolons 289 | func main() { 290 | // Prints text and a newline 291 | // List package name followed by a period and the function name 292 | pl("Hello Go") 293 | 294 | // Get user input (To run this in the terminal go run hellogo.go) 295 | pl("What is your name?") 296 | // Setup buffered reader that gets text from the keyboard 297 | reader := bufio.NewReader(os.Stdin) 298 | // Copy text up to the newline 299 | // The blank identifier _ will get err and ignore it (Bad Practice) 300 | // name, _ := reader.ReadString('\n') 301 | // It is better to handle it 302 | name, err := reader.ReadString('\n') 303 | if err == nil { 304 | pl("Hello", name) 305 | } else { 306 | // Log this error 307 | log.Fatal(err) 308 | } 309 | 310 | // ----- VARIABLES ----- 311 | // var name type 312 | // Name must begin with letter and then letters or numbers 313 | // If a variable, function or type starts with a capital letter 314 | // it is considered exported and can be accessed outside the 315 | // package and otherwise is available only in the current package 316 | // Camal case is the default naming convention 317 | 318 | // var vName string = "Derek" 319 | // var v1, v2 = 1.2, 3.4 320 | 321 | // Short variable declaration (Type defined by data) 322 | // var v3 = "Hello" 323 | 324 | // Variables are mutable by default (Value can change as long 325 | // as the data type is the same) 326 | // v1 := 2.4 327 | 328 | // After declaring variables to assign values to them always use 329 | // = there after. If you use := you'll create a new variable 330 | 331 | // ----- DATA TYPES ----- 332 | // int, float64, bool, string, rune 333 | // Default type 0, 0.0, false, "" 334 | pl(reflect.TypeOf(25)) 335 | pl(reflect.TypeOf(3.14)) 336 | pl(reflect.TypeOf(true)) 337 | pl(reflect.TypeOf("Hello")) 338 | pl(reflect.TypeOf('🦍')) 339 | 340 | // ----- CASTING ----- 341 | // To cast type the type to convert to with the variable to 342 | // convert in parentheses 343 | // Doesn't work with bools or strings 344 | cV1 := 1.5 345 | cV2 := int(cV1) 346 | pl(cV2) 347 | 348 | // Convert string to int (ASCII to Integer) 349 | // Returns the result with an error if any 350 | cV3 := "50000000" 351 | cV4, err := strconv.Atoi(cV3) 352 | pl(cV4, err, reflect.TypeOf(cV4)) 353 | 354 | // Convert int to string (Integer to ASCII) 355 | cV5 := 50000000 356 | cV6 := strconv.Itoa(cV5) 357 | pl(cV6) 358 | 359 | // Convert string to float 360 | cV7 := "3.14" 361 | // Handling potential errors (Prints if err == nil) 362 | if cV8, err := strconv.ParseFloat(cV7, 64); err == nil { 363 | pl(cV8) 364 | } 365 | 366 | // Use Sprintf to convert from float to string 367 | cV9 := fmt.Sprintf("%f", 3.14) 368 | pl(cV9) 369 | 370 | // ----- IF CONDITIONAL ----- 371 | // Conditional Operators : > < >= <= == != 372 | // Logical Operators : && || ! 373 | iAge := 8 374 | if (iAge >= 1) && (iAge <= 18) { 375 | pl("Important Birthday") 376 | } else if (iAge == 21) || (iAge == 50) { 377 | pl("Important Birthday") 378 | } else if iAge >= 65 { 379 | pl("Important Birthday") 380 | } else { 381 | pl("Not and Important Birthday") 382 | } 383 | 384 | // ! turns bools into their opposite value 385 | pl("!true =", !true) 386 | 387 | // ----- STRINGS ----- 388 | // Strings are arrays of bytes []byte 389 | // Escape Sequences : \n \t \" \\ 390 | sV1 := "A word" 391 | 392 | // Replacer that can be used on multiple strings 393 | // to replace one string with another 394 | replacer := strings.NewReplacer("A", "Another") 395 | sV2 := replacer.Replace(sV1) 396 | pl(sV2) 397 | 398 | // Get length 399 | pl("Length : ", len(sV2)) 400 | 401 | // Contains string 402 | pl("Contains Another :", strings.Contains(sV2, "Another")) 403 | 404 | // Get first index match 405 | pl("o index :", strings.Index(sV2, "o")) 406 | 407 | // Replace all matches with 0 408 | // If -1 was 2 it would replace the 1st 2 matches 409 | pl("Replace :", strings.Replace(sV2, "o", "0", -1)) 410 | 411 | // Remove whitespace characters from beginning and end of string 412 | sV3 := "\nSome words\n" 413 | sV3 = strings.TrimSpace(sV3) 414 | 415 | // Split at delimiter 416 | pl("Split :", strings.Split("a-b-c-d", "-")) 417 | 418 | // Upper and lowercase string 419 | pl("Lower :", strings.ToLower(sV2)) 420 | pl("Upper :", strings.ToUpper(sV2)) 421 | 422 | // Prefix or suffix 423 | pl("Prefix :", strings.HasPrefix("tacocat", "taco")) 424 | pl("Suffix :", strings.HasSuffix("tacocat", "cat")) 425 | 426 | // ----- RUNES ----- 427 | // In Go characters are called Runes 428 | // Runes are unicodes that represent characters 429 | rStr := "abcdefg" 430 | 431 | // Runes in string 432 | pl("Rune Count :", utf8.RuneCountInString(rStr)) 433 | 434 | // Print runes in string 435 | for i, runeVal := range rStr { 436 | // Get index, Rune unicode and character 437 | fmt.Printf("%d : %#U : %c\n", i, runeVal, runeVal) 438 | } 439 | 440 | // ----- TIME ----- 441 | // Get day, month, year and time data 442 | // Get current time 443 | now := time.Now() 444 | pl(now.Year(), now.Month(), now.Day()) 445 | pl(now.Hour(), now.Minute(), now.Second()) 446 | 447 | // ----- MATH ----- 448 | pl("5 + 4 =", 5+4) 449 | pl("5 - 4 =", 5-4) 450 | pl("5 * 4 =", 5*4) 451 | pl("5 / 4 =", 5/4) 452 | pl("5 % 4 =", 5%4) 453 | 454 | // Shorthand increment 455 | // Instead of mInt = mInt + 1 (mInt += 1) 456 | // -= *= /= 457 | mInt := 1 458 | mInt += 1 459 | // Also increment or decrement with ++ and -- 460 | mInt++ 461 | 462 | // Float precision increases with the size of your values 463 | pl("Float Precision =", 0.11111111111111111111+ 464 | 0.11111111111111111111) 465 | 466 | // Create a random value between 0 and 50 467 | // Get a seed value for our random number generator based on 468 | // seconds since 1/1/70 to make our random number more random 469 | seedSecs := time.Now().Unix() // Returns seconds as int 470 | rand.Seed(seedSecs) 471 | randNum := rand.Intn(50) + 1 472 | pl("Random :", randNum) 473 | 474 | // There are many math functions 475 | pl("Abs(-10) =", math.Abs(-10)) 476 | pl("Pow(4, 2) =", math.Pow(4, 2)) 477 | pl("Sqrt(16) =", math.Sqrt(16)) 478 | pl("Cbrt(8) =", math.Cbrt(8)) 479 | pl("Ceil(4.4) =", math.Ceil(4.4)) 480 | pl("Floor(4.4) =", math.Floor(4.4)) 481 | pl("Round(4.4) =", math.Round(4.4)) 482 | pl("Log2(8) =", math.Log2(8)) 483 | pl("Log10(100) =", math.Log10(100)) 484 | // Get the log of e to the power of 2 485 | pl("Log(7.389) =", math.Log(math.Exp(2))) 486 | pl("Max(5,4) =", math.Max(5, 4)) 487 | pl("Min(5,4) =", math.Min(5, 4)) 488 | 489 | // Convert 90 degrees to radians 490 | r90 := 90 * math.Pi / 180 491 | // Convert 1.5708 radians to degrees 492 | d90 := r90 * (180 / math.Pi) 493 | fmt.Printf("%f radians = %f degrees\n", r90, d90) 494 | pl("Sin(90) =", math.Sin(r90)) 495 | 496 | // There are also functions for Cos, Tan, Acos, Asin 497 | // Atan, Asinh, Acosh, Atanh, Atan2, Cosh, Sinh, Sincos 498 | // Htpot 499 | 500 | // ----- FORMATTED PRINT ----- 501 | // Go has its own version of C's printf 502 | // %d : Integer 503 | // %c : Character 504 | // %f : Float 505 | // %t : Boolean 506 | // %s : String 507 | // %o : Base 8 508 | // %x : Base 16 509 | // %v : Guesses based on data type 510 | // %T : Type of supplied value 511 | 512 | fmt.Printf("%s %d %c %f %t %o %x\n", "Stuff", 1, 'A', 513 | 3.14, true, 1, 1) 514 | 515 | // Float formatting 516 | fmt.Printf("%9f\n", 3.14) // Width 9 517 | fmt.Printf("%.2f\n", 3.141592) // Decimal precision 2 518 | fmt.Printf("%9.f\n", 3.141592) // Width 9 no precision 519 | 520 | // Sprintf returns a formatted string instead of printing 521 | sp1 := fmt.Sprintf("%9.f\n", 3.141592) 522 | pl(sp1) 523 | 524 | // ----- FOR LOOPS ----- 525 | // for initialization; condition; postStatement {BODY} 526 | // Print numbers 1 through 5 527 | for x := 1; x <= 5; x++ { 528 | pl(x) 529 | } 530 | // Do the opposite 531 | for x := 5; x >= 1; x-- { 532 | pl(x) 533 | } 534 | 535 | // x is out of the scope of the for loop so it doesn't exist 536 | // pl("x :", x) 537 | 538 | // For is used to create while loops as well 539 | fX := 0 540 | for fX < 5 { 541 | pl(fX) 542 | fX++ 543 | } 544 | 545 | // While true loop (Infinite Loop) will be used for a guessing 546 | // game 547 | seedSecs := time.Now().Unix() // Returns seconds as int 548 | rand.Seed(seedSecs) 549 | randNum := rand.Intn(50) + 1 550 | for true { 551 | fmt.Print("Guess a number between 0 and 50 : ") 552 | pl("Random Number is :", randNum) 553 | guess, err := reader.ReadString('\n') 554 | if err != nil { 555 | log.Fatal(err) 556 | } 557 | guess = strings.TrimSpace(guess) 558 | iGuess, err := strconv.Atoi(guess) 559 | if err != nil { 560 | log.Fatal(err) 561 | } 562 | if iGuess > randNum { 563 | pl("Lower") 564 | } else if iGuess < randNum { 565 | pl("Higher") 566 | } else { 567 | pl("You Guessed it") 568 | break 569 | } 570 | 571 | // Cycle through an array with range 572 | // More on arrays later 573 | // We don't need the index so we ignore it 574 | // with the blank identifier _ 575 | aNums := []int{1, 2, 3} 576 | for _, num := range aNums { 577 | pl(num) 578 | } 579 | } 580 | 581 | // ----- ARRAYS ----- 582 | // Collection of values with the same data type 583 | // and the size can't be changed 584 | // Default values are 0, 0.0, false or "" 585 | 586 | // Declare integer array with 5 elements 587 | var arr1 [5]int 588 | 589 | // Assign value to index 590 | arr1[0] = 1 591 | 592 | // Declare and initialize 593 | arr2 := [5]int{1, 2, 3, 4, 5} 594 | 595 | // Get by index 596 | pl("Index 0 :", arr2[0]) 597 | 598 | // Length 599 | pl("Arr Length :", len(arr2)) 600 | 601 | // Iterate with index 602 | for i := 0; i < len(arr2); i++ { 603 | pl(arr2[i]) 604 | } 605 | 606 | // Iterate with range 607 | for i, v := range arr2 { 608 | fmt.Printf("%d : %d", i, v) 609 | } 610 | 611 | // Multidimensional Array 612 | arr3 := [2][2]int{ 613 | {1, 2}, 614 | {3, 4}, 615 | } 616 | 617 | // Print multidimensional array 618 | for i := 0; i < 2; i++ { 619 | for j := 0; j < 2; j++ { 620 | pl(arr3[i][j]) 621 | } 622 | } 623 | 624 | // String into slice of runes 625 | aStr1 := "abcde" 626 | rArr := []rune(aStr1) 627 | for _, v := range rArr { 628 | fmt.Printf("Rune Array : %d\n", v) 629 | } 630 | 631 | // Byte array to string 632 | byteArr := []byte{'a', 'b', 'c'} 633 | bStr := string(byteArr[:]) 634 | pl("I'm a string :", bStr) 635 | 636 | // ----- SLICES ----- 637 | // Slices are like arrays but they can grow 638 | // var name []dataType 639 | // Create a slice with make 640 | sl1 := make([]string, 6) 641 | 642 | // Assign values by index 643 | sl1[0] = "Society" 644 | sl1[1] = "of" 645 | sl1[2] = "the" 646 | sl1[3] = "Simulated" 647 | sl1[4] = "Universe" 648 | 649 | // Size of slice 650 | pl("Slice Size :", len(sl1)) 651 | 652 | // Cycle with for 653 | for i := 0; i < len(sl1); i++ { 654 | pl(sl1[i]) 655 | } 656 | 657 | // Cycle with range 658 | for _, x := range sl1 { 659 | pl(x) 660 | } 661 | 662 | // Create a slice literal 663 | sl2 := []int{12, 21, 1974} 664 | pl(sl2) 665 | 666 | // A slice points at an array and you can create a slice 667 | // of an array (A slice is a view of an underlying array) 668 | // You can have multiple slices point to the same array 669 | sArr := [5]int{1, 2, 3, 4, 5} 670 | // Start at 0 index up to but not including the 2nd index 671 | sl3 := sArr[0:2] 672 | pl(sl3) 673 | 674 | // Get slice from beginning 675 | pl("1st 3 :", sArr[:3]) 676 | 677 | // Get slice to the end 678 | pl("Last 3 :", sArr[2:]) 679 | 680 | // If you change the array the slice also changes 681 | sArr[0] = 10 682 | pl("sl3 :", sl3) 683 | 684 | // Changing the slice also changes the array 685 | sl3[0] = 1 686 | pl("sArr :", sArr) 687 | 688 | // Append a value to a slice (Also overwrites array) 689 | sl3 = append(sl3, 12) 690 | pl("sl3 :", sl3) 691 | pl("sArr :", sArr) 692 | 693 | // Printing empty slices will return nils which show 694 | // as empty slices 695 | sl4 := make([]string, 6) 696 | pl("sl4 :", sl4) 697 | pl("sl4[0] :", sl4[0]) 698 | 699 | // ----- FUNCTIONS ----- 700 | // func funcName(parameters) returnType {BODY} 701 | // If you only need a function in the current package 702 | // start with lowercase letter 703 | // Letters and numbers in camelcase 704 | sayHello() 705 | pl(getSum(5, 4)) 706 | f1, f2 := getTwo(5) 707 | fmt.Printf("%d %d\n", f1, f2) 708 | 709 | // Function that can return an error 710 | ans, err := getQuotient(5, 0) 711 | if err == nil { 712 | pl("5/4 =", ans) 713 | } else { 714 | pl(err) 715 | // End program 716 | // log.Fatal(err) 717 | } 718 | 719 | // Function receives unknown number of parameters 720 | // Variadic Function 721 | pl("Unknown Sum :", getSum2(1, 2, 3, 4)) 722 | 723 | // Pass an array to a function by value 724 | vArr := []int{1, 2, 3, 4} 725 | pl("Array Sum :", getArraySum(vArr)) 726 | 727 | // Go passes the value to functions so it isn't changed 728 | // even if the same variable name is used 729 | f3 := 5 730 | pl("f3 before func :", f3) 731 | changeVal(f3) 732 | pl("f3 after func :", f3) 733 | 734 | // ----- POINTERS ----- 735 | 736 | // You can pass by reference with the & 737 | // (Address of Operator) 738 | // Print amount and address for amount in memory 739 | f4 := 10 740 | pl("f4 :", f4) 741 | pl("f4 Address :", &f4) 742 | 743 | // Store a pointer (Pointer to int) 744 | var f4Ptr *int = &f4 745 | pl("f4 Address :", f4Ptr) 746 | 747 | // Print value at pointer 748 | pl("f4 Value :", *f4Ptr) 749 | 750 | // Assign value using pointer 751 | *f4Ptr = 11 752 | pl("f4 Value :", *f4Ptr) 753 | 754 | // Change value in function 755 | pl("f4 before function :", f4) 756 | changeVal2(&f4) 757 | pl("f4 after function :", f4) 758 | 759 | // Pass an array by reference 760 | pArr := [4]int{1, 2, 3, 4} 761 | dblArrVals(&pArr) 762 | pl(pArr) 763 | 764 | // Passing a slice to a function works just 765 | // like when using variadic functions 766 | // Just add ... after the slice when passing 767 | iSlice := []float64{11, 13, 17} 768 | fmt.Printf("Average : %.3f\n", getAverage(iSlice...)) 769 | 770 | // ----- FILE IO ----- 771 | // We can create, write and read from files 772 | 773 | // Create a file 774 | f, err := os.Create("data.txt") 775 | if err != nil { 776 | log.Fatal(err) 777 | } 778 | 779 | // Says to close the file after program ends or when 780 | // there is a closing curly bracket 781 | defer f.Close() 782 | 783 | // Create list of primes 784 | iPrimeArr := []int{2, 3, 5, 7, 11} 785 | // Create string array from int array 786 | var sPrimeArr []string 787 | for _, i := range iPrimeArr { 788 | sPrimeArr = append(sPrimeArr, strconv.Itoa(i)) 789 | } 790 | 791 | // Cycle through strings and write to file 792 | for _, num := range sPrimeArr { 793 | _, err := f.WriteString(num + "\n") 794 | 795 | if err != nil { 796 | log.Fatal(err) 797 | } 798 | } 799 | 800 | // Open the created file 801 | f, err = os.Open("data.txt") 802 | if err != nil { 803 | log.Fatal(err) 804 | } 805 | defer f.Close() 806 | 807 | // Read from file and print once per line 808 | scan1 := bufio.NewScanner(f) 809 | for scan1.Scan() { 810 | pl("Prime :", scan1.Text()) 811 | } 812 | if err := scan1.Err(); err != nil { 813 | log.Fatal(err) 814 | } 815 | 816 | // Append to file 817 | /* 818 | Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified 819 | 820 | O_RDONLY : open the file read-only 821 | O_WRONLY : open the file write-only 822 | O_RDWR : open the file read-write 823 | 824 | These can be or'ed 825 | 826 | O_APPEND : append data to the file when writing 827 | O_CREATE : create a new file if none exists 828 | O_EXCL : used with O_CREATE, file must not exist 829 | O_SYNC : open for synchronous I/O 830 | O_TRUNC : truncate regular writable file when opened 831 | */ 832 | 833 | // Check if file exists 834 | _, err = os.Stat("data.txt") 835 | if errors.Is(err, os.ErrNotExist) { 836 | pl("File Doesn't Exist") 837 | } else { 838 | f, err = os.OpenFile("data.txt", 839 | os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 840 | if err != nil { 841 | log.Fatal(err) 842 | } 843 | defer f.Close() 844 | if _, err := f.WriteString("13\n"); err != nil { 845 | log.Fatal(err) 846 | } 847 | } 848 | 849 | // ----- COMMAND LINE ARGUMENTS ----- 850 | // You can pass values to your program 851 | // from the command line 852 | // Create cltest.go 853 | // go build cltest.go 854 | // .\cltest 24 43 12 9 10 855 | // Returns an array with everything 856 | // passed with the name of the app 857 | // in the first index 858 | // Outputs the max number passed in 859 | 860 | // ----- PACKAGES ----- 861 | // Packages allow you to keep related code together 862 | // Go looks for package code in a directory 863 | 864 | // If you are using VSC and have multiple 865 | // modules you get this error 866 | // gopls requires a module at the root of 867 | // your workspace 868 | // 1. Settings 869 | // 2. In search type gopls 870 | // 3. Paste "gopls": { "experimentalWorkspaceModule": true, } 871 | // 4. Restart VSC 872 | 873 | // cd /D D:\Tutorials\GoTutorial 874 | 875 | // Create a go directory : mkdir app 876 | // cd app 877 | // Choose a module path and create a go.mod file 878 | // go mod init example/project 879 | 880 | // Go modules allow you to manage libraries 881 | // They contain one project or library and a 882 | // collection of Go packages 883 | // go.mod : contains the name of the module and versions 884 | // of other modules your module depends on 885 | 886 | // Create a main.go file at the same level as go.mod 887 | 888 | // You can have many packages and sub packages 889 | // create a directory called mypackage in the project 890 | // directory mkdir mypackage 891 | // cd mypackage 892 | 893 | // Create file mypackage.go in it 894 | 895 | // Package names should be all lowercase 896 | 897 | // ----- MAPS ----- 898 | // Maps are collections of key/value pairs 899 | // Keys can be any data type that can be compared 900 | // using == (They can be a different type than 901 | // the value) 902 | // var myMap map [keyType]valueType 903 | 904 | // Declare a map variable 905 | var heroes map[string]string 906 | // Create the map 907 | heroes = make(map[string]string) 908 | 909 | // You can do it in one step 910 | villians := make(map[string]string) 911 | 912 | // Add keys and values 913 | heroes["Batman"] = "Bruce Wayne" 914 | heroes["Superman"] = "Clark Kent" 915 | heroes["The Flash"] = "Barry Allen" 916 | villians["Lex Luther"] = "Lex Luther" 917 | 918 | // Define with map literal 919 | superPets := map[int]string{1: "Krypto", 920 | 2: "Bat Hound"} 921 | 922 | // Get value with key (Use %v with Printf) 923 | fmt.Printf("Batman is %v\n", heroes["Batman"]) 924 | 925 | // If you access a key that doesn't exist 926 | // you get nil 927 | pl("Chip :", superPets[3]) 928 | 929 | // You can check if there is a value or nil 930 | _, ok := superPets[3] 931 | pl("Is there a 3rd pet :", ok) 932 | 933 | // Cycle through map 934 | for k, v := range heroes { 935 | fmt.Printf("%s is %s\n", k, v) 936 | } 937 | 938 | // Delete a key value 939 | delete(heroes, "The Flash") 940 | 941 | // ----- GENERICS ----- 942 | // We can specify the data type to be used at a 943 | // later time with generics 944 | // It is mainly used when we want to create 945 | // functions that can work with 946 | // multiple data types 947 | pl("5 + 4 =", getSumGen(5, 4)) 948 | pl("5.6 + 4.7 =", getSumGen(5.6, 4.7)) 949 | 950 | // This causes an error 951 | // pl("5.6 + 4.7 =", getSumGen("5.6", "4.7")) 952 | 953 | // ----- STRUCTS ----- 954 | // Structs allow you to store values with many 955 | // data types 956 | 957 | // Add values 958 | var tS customer 959 | tS.name = "Tom Smith" 960 | tS.address = "5 Main St" 961 | tS.bal = 234.56 962 | 963 | // Pass to function as values 964 | getCustInfo(tS) 965 | // or as reference 966 | newCustAdd(&tS, "123 South st") 967 | pl("Address :", tS.address) 968 | 969 | // Create a struct literal 970 | sS := customer{"Sally Smith", "123 Main", 0.0} 971 | pl("Name :", sS.name) 972 | 973 | // Structs with functions 974 | rect1 := rectangle{10.0, 15.0} 975 | pl("Rect Area :", rect1.Area()) 976 | 977 | // Go doesn't support inheritance, but it does 978 | // support composition by embedding a struct 979 | // in another 980 | con1 := contact{ 981 | "James", 982 | "Wang", 983 | "555-1212", 984 | } 985 | 986 | bus1 := business{ 987 | "ABC Plumbing", 988 | "234 North St", 989 | con1, 990 | } 991 | 992 | bus1.info() 993 | 994 | // ----- DEFINED TYPES ----- 995 | // We used a defined type previously with structs 996 | // You can use them also to enhance the quality 997 | // of other data types 998 | // We'll create them for different measurements 999 | 1000 | // Convert from tsp to mL 1001 | ml1 := ML(Tsp(3) * 4.92) 1002 | fmt.Printf("3 tsps = %.2f mL\n", ml1) 1003 | 1004 | // Convert from TBs to mL 1005 | ml2 := ML(TBs(3) * 14.79) 1006 | fmt.Printf("3 TBs = %.2f mL\n", ml2) 1007 | 1008 | // You can use arithmetic and comparison 1009 | // operators 1010 | pl("2 tsp + 4 tsp =", Tsp(2), Tsp(4)) 1011 | pl("2 tsp > 4 tsp =", Tsp(2) > Tsp(4)) 1012 | 1013 | // We can convert with functions 1014 | // Bad Way 1015 | fmt.Printf("3 tsp = %.2f mL\n", tspToML(3)) 1016 | fmt.Printf("3 TBs = %.2f mL\n", TBToML(3)) 1017 | 1018 | // We can solve this by using methods which 1019 | // are functions associated with a type 1020 | tsp1 := Tsp(3) 1021 | fmt.Printf("%.2f tsp = %.2f mL\n", tsp1, tsp1.ToMLs()) 1022 | 1023 | // ----- PROTECTING DATA ----- 1024 | // We want to protect our data from receiving 1025 | // bad values by moving our date struct 1026 | // to another package using encapsulation 1027 | // We'll use mypackage like before 1028 | 1029 | // ----- INTERFACES ----- 1030 | // Interfaces allow you to create contracts 1031 | // that say if anything inherits it that 1032 | // they will implement defined methods 1033 | 1034 | // If we had animals and wanted to define that 1035 | // they all perform certain actions, but in their 1036 | // specific way we could use an interface 1037 | 1038 | // With Go you don't have to say a type uses 1039 | // an interface. When your type implements 1040 | // the required methods it is automatic 1041 | var kitty Animal 1042 | kitty = Cat("Kitty") 1043 | kitty.AngrySound() 1044 | 1045 | // We can only call methods defined in the 1046 | // interface for Cats because of the contract 1047 | // unless you convert Cat back into a concrete 1048 | // Cat type using a type assertion 1049 | var kitty2 Cat = kitty.(Cat) 1050 | kitty2.Attack() 1051 | pl("Cats Name :", kitty2.Name()) 1052 | 1053 | // ----- CONCURRENCY ----- 1054 | // Concurrency allows us to have multiple 1055 | // blocks of code share execution time by 1056 | // pausing their execution. We can also 1057 | // run blocks of codes in parallel at the same 1058 | // time. (Concurrent tasks in Go are called 1059 | // goroutines) 1060 | 1061 | // To execute multiple functions in new 1062 | // goroutines add the word go in front of 1063 | // the function calls (Those functions can't 1064 | // have return values) 1065 | 1066 | // We can't control when functions execute 1067 | // so we may get different results 1068 | go printTo15() 1069 | go printTo10() 1070 | 1071 | // We have to pause the main function because 1072 | // if main ends so will the goroutines 1073 | time.Sleep(2 * time.Second) // Pause 2 seconds 1074 | 1075 | // You can have goroutines communicate by 1076 | // using channels. The sending goroutine 1077 | // also makes sure the receiving goroutine 1078 | // receives the value before it attempts 1079 | // to use it 1080 | 1081 | // Create a channel : Only carries values of 1082 | // 1 type 1083 | channel1 := make(chan int) 1084 | channel2 := make(chan int) 1085 | go nums1(channel1) 1086 | go nums2(channel2) 1087 | pl(<-channel1) 1088 | pl(<-channel1) 1089 | pl(<-channel1) 1090 | pl(<-channel2) 1091 | pl(<-channel2) 1092 | pl(<-channel2) 1093 | 1094 | // Using locks to protect data from being 1095 | // accessed by more than one user at a time 1096 | // Locks are another option when you don't 1097 | // have to pass data 1098 | var acct Account 1099 | acct.balance = 100 1100 | pl("Balance :", acct.GetBalance()) 1101 | 1102 | for i := 0; i < 12; i++ { 1103 | go acct.Withdraw(10) 1104 | } 1105 | time.Sleep(2 * time.Second) 1106 | 1107 | // ----- CLOSURES ----- 1108 | // Closures are functions that don't have to be 1109 | // associated with an identifier (Anonymous) 1110 | 1111 | // Create a closure that sums values 1112 | intSum := func(x, y int) int { return x + y } 1113 | pl("5 + 4 =", intSum(5, 4)) 1114 | 1115 | // Closures can change values outside the function 1116 | samp1 := 1 1117 | changeVar := func() { samp1 += 1 } 1118 | changeVar() 1119 | pl("samp1 =", samp1) 1120 | 1121 | // Pass a function to a function 1122 | useFunc(sumVals, 5, 8) 1123 | 1124 | // ----- RECURSION ----- 1125 | // Recursion occurs when a function calls itself 1126 | // There must be a condition that ends this 1127 | // Finding a factorial is commonly used 1128 | pl("Factorial 4 =", factorial(4)) 1129 | // 1st : result = 4 * factorial(3) = 4 * 6 = 24 1130 | // 2nd : result = 3 * factorial(2) = 3 * 2 = 6 1131 | // 3rd : result = 2 * factorial(1) = 2 * 1 = 2 1132 | 1133 | // ----- REGULAR EXPRESSIONS ----- 1134 | // You can use regular expressions to test 1135 | // if a string matches a pattern 1136 | 1137 | // Search for ape followed by not a space 1138 | reStr := "The ape was at the apex" 1139 | match, _ := regexp.MatchString("(ape[^ ]?)", reStr) 1140 | pl(match) 1141 | 1142 | // You can compile them 1143 | // Find multiple words ending with at 1144 | reStr2 := "Cat rat mat fat pat" 1145 | r, _ := regexp.Compile("([crmfp]at)") 1146 | 1147 | // Did you find any matches? 1148 | pl("MatchString :", r.MatchString(reStr2)) 1149 | 1150 | // Return first match 1151 | pl("FindString :", r.FindString(reStr2)) 1152 | 1153 | // Starting and ending index for 1st match 1154 | pl("Index :", r.FindStringIndex(reStr2)) 1155 | 1156 | // Return all matches 1157 | pl("All String :", r.FindAllString(reStr2, -1)) 1158 | 1159 | // Get 1st 2 matches 1160 | pl("All String :", r.FindAllString(reStr2, 2)) 1161 | 1162 | // Get indexes for all matches 1163 | pl("All Submatch Index :", r.FindAllStringSubmatchIndex(reStr2, -1)) 1164 | 1165 | // Replace all matches with Dog 1166 | pl(r.ReplaceAllString(reStr2, "Dog")) 1167 | 1168 | // ----- AUTOMATED TESTING ----- 1169 | // Automated tests make sure your program still 1170 | // works while you change the code 1171 | // Create app2 directory with testemail.go 1172 | // cd app2 1173 | // Create testemail_test.go 1174 | // go mod init app2 1175 | // Run Tests : go test -v 1176 | 1177 | // ----- GO ON THE WEB ----- 1178 | // Our Go apps can run in the browser 1179 | // Create directory webapp with webapp.go 1180 | // Run our app with go run webapp.go 1181 | // Ctrl + c to stop server 1182 | } 1183 | -------------------------------------------------------------------------------- /webapp/new.html: -------------------------------------------------------------------------------- 1 |
{{.}}
15 | {{end}} 16 |