23 | Gopher notes is an archive of Go notes found online and across open
24 | source materials. It's developed under the intention of helping
25 | aspiring Go developers. If it's your first time using the guide, read
26 | the{' '}
27 |
31 | requirements section
32 | {' '}
33 | and go over the{' '}
34 |
38 | intro part
39 |
40 | .
41 |
42 |
43 | The contents may subject to the respective authors and License. It's
44 | free for personal use but prohibited to publish the contents neither
45 | for commercial use nor if it falls outside of the original authors'
46 | permission.
47 |
22 |
23 | );
24 | };
25 |
26 | export default GitHubButton;
27 |
--------------------------------------------------------------------------------
/src/data/advanced-functions/anonymous-functions.md:
--------------------------------------------------------------------------------
1 | # Anonymous Functions
2 |
3 | Anonymous functions are form of functions in that they have *no name*. Anonymous functions are useful when defining a function that will only be used once or to create a quick [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)).
4 |
5 | ```go
6 | // doMath accepts a function that converts one int into another
7 | // and a slice of ints. It returns a slice of ints that have been
8 | // converted by the passed in function.
9 | func doMath(f func(int) int, nums []int) []int {
10 | var results []int
11 | for _, n := range nums {
12 | results = append(results, f(n))
13 | }
14 | return results
15 | }
16 |
17 | func main() {
18 | nums := []int{1, 2, 3, 4, 5}
19 |
20 | // Here we define an anonymous function that doubles an int
21 | // and pass it to doMath
22 | allNumsDoubled := doMath(func(x int) int {
23 | return x + x
24 | }, nums)
25 |
26 | fmt.Println(allNumsDoubled)
27 | // prints:
28 | // [2 4 6 8 10]
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/src/data/advanced-functions/closures.md:
--------------------------------------------------------------------------------
1 | # Closures
2 |
3 | A closure is a function that references variables from outside its own function body. The function may access and *assign* to the referenced variables.
4 |
5 | In this example, the `concatter()` function returns a function that has reference to an *enclosed* `doc` value. Each successive call to `harryPotterAggregator` mutates that same `doc` variable.
6 |
7 | ```go
8 | func concatter() func(string) string {
9 | doc := ""
10 | return func(word string) string {
11 | doc += word + " "
12 | return doc
13 | }
14 | }
15 |
16 | func main() {
17 | harryPotterAggregator := concatter()
18 | harryPotterAggregator("Mr.")
19 | harryPotterAggregator("and")
20 | harryPotterAggregator("Mrs.")
21 | harryPotterAggregator("Dursley")
22 | harryPotterAggregator("of")
23 | harryPotterAggregator("number")
24 | harryPotterAggregator("four,")
25 | harryPotterAggregator("Privet")
26 |
27 | fmt.Println(harryPotterAggregator("Drive"))
28 | // Mr. and Mrs. Dursley of number four, Privet Drive
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/src/data/advanced-functions/currying.md:
--------------------------------------------------------------------------------
1 | # Currying
2 |
3 | Function currying is the practice of writing a function that takes a function (or functions) as input, and returns a new function.
4 |
5 | For example:
6 |
7 | ```go
8 | func main() {
9 | squareFunc := selfMath(multiply)
10 | doubleFunc := selfMath(add)
11 |
12 | fmt.Println(squareFunc(5))
13 | // prints 25
14 |
15 | fmt.Println(doubleFunc(5))
16 | // prints 10
17 | }
18 |
19 | func multiply(x, y int) int {
20 | return x * y
21 | }
22 |
23 | func add(x, y int) int {
24 | return x + y
25 | }
26 |
27 | func selfMath(mathFunc func(int, int) int) func (int) int {
28 | return func(x int) int {
29 | return mathFunc(x, x)
30 | }
31 | }
32 | ```
33 |
34 | In the example above, the `selfMath` function takes in a function as its parameter, and returns a function that itself returns the value of running that input function on its parameter.
35 |
--------------------------------------------------------------------------------
/src/data/advanced-functions/defer.md:
--------------------------------------------------------------------------------
1 | # Defer
2 |
3 | The `defer` keyword is a fairly unique feature of Go. It allows a function to be executed automatically *just before* its enclosing function returns.
4 |
5 | The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
6 |
7 | Deferred functions are typically used to close database connections, file handlers and the like.
8 |
9 | For example:
10 |
11 | ```go
12 | // CopyFile copies a file from srcName to dstName on the local filesystem.
13 | func CopyFile(dstName, srcName string) (written int64, err error) {
14 |
15 | // Open the source file
16 | src, err := os.Open(srcName)
17 | if err != nil {
18 | return
19 | }
20 | // Close the source file when the CopyFile function returns
21 | defer src.Close()
22 |
23 | // Create the destination file
24 | dst, err := os.Create(dstName)
25 | if err != nil {
26 | return
27 | }
28 | // Close the destination file when the CopyFile function returns
29 | defer dst.Close()
30 |
31 | return io.Copy(dst, src)
32 | }
33 | ```
34 |
35 | In the above example, the `src.Close()` function is not called until after the `CopyFile` function was called but immediately before the `CopyFile` function returns.
36 |
37 | Defer is a great way to **make sure** that something happens at the end of a function, even if there are multiple return statements.
--------------------------------------------------------------------------------
/src/data/advanced-functions/higher-order-functions.md:
--------------------------------------------------------------------------------
1 | # First Class and Higher Order Functions
2 |
3 | A programming language is said to have "first-class functions" when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.
4 |
5 | A function that returns a function or accepts a function as input is called a Higher-Order Function.
6 |
7 | Go supports [first-class](https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function) and higher-order functions. Another way to think of this is that a function is just another type -- just like `int`s and `string`s and `bool`s.
8 |
9 | For example, to accept a function as a parameter:
10 |
11 | ```go
12 | func add(x, y int) int {
13 | return x + y
14 | }
15 |
16 | func mul(x, y int) int {
17 | return x * y
18 | }
19 |
20 | // aggregate applies the given math function to the first 3 inputs
21 | func aggregate(a, b, c int, arithmetic func(int, int) int) int {
22 | return arithmetic(arithmetic(a, b), c)
23 | }
24 |
25 | func main(){
26 | fmt.Println(aggregate(2,3,4, add))
27 | // prints 9
28 | fmt.Println(aggregate(2,3,4, mul))
29 | // prints 24
30 | }
31 | ```
32 |
--------------------------------------------------------------------------------
/src/data/advanced-functions/pointer-receivers.md:
--------------------------------------------------------------------------------
1 | # Pointer Receivers
2 |
3 | A receiver type on a method can be a pointer.
4 |
5 | Methods with pointer receivers can modify the value to which the receiver points. Since methods often need to modify their receiver, pointer receivers are *more common* than value receivers.
6 |
7 | ## Pointer Receiver
8 |
9 | ```go
10 | type car struct {
11 | color string
12 | }
13 |
14 | func (c *car) setColor(color string) {
15 | c.color = color
16 | }
17 |
18 | func main() {
19 | c := car{
20 | color: "white",
21 | }
22 | c.setColor("blue")
23 | fmt.Println(c.color)
24 | // prints "blue"
25 | }
26 | ```
27 |
28 | ## Non-pointer Receiver
29 |
30 | ```go
31 | type car struct {
32 | color string
33 | }
34 |
35 | func (c car) setColor(color string) {
36 | c.color = color
37 | }
38 |
39 | func main() {
40 | c := car{
41 | color: "white",
42 | }
43 | c.setColor("blue")
44 | fmt.Println(c.color)
45 | // prints "white"
46 | }
47 | ```
48 |
49 | ## Pointer Receiver Code
50 |
51 | Methods with pointer receivers don't require that a pointer is used to call the method. The pointer will automatically be derived from the value.
52 |
53 | ```go
54 | type circle struct {
55 | x int
56 | y int
57 | radius int
58 | }
59 |
60 | func (c *circle) grow(){
61 | c.radius *= 2
62 | }
63 |
64 | func main(){
65 | c := circle{
66 | x: 1,
67 | y: 2,
68 | radius: 4,
69 | }
70 |
71 | // notice c is not a pointer in the calling function
72 | // but the method still gains access to a pointer to c
73 | c.grow()
74 | fmt.Println(c.radius)
75 | // prints 8
76 | }
77 | ```
78 |
--------------------------------------------------------------------------------
/src/data/advanced-functions/pointers-intro.md:
--------------------------------------------------------------------------------
1 | # Introduction to Pointers
2 |
3 | As we know, a variable is a named location in memory that stores a value. We can manipulate the value of a variable by assigning a new value to it or by performing operations on it. When we assign a value to a variable, we are storing that value in a specific location in memory.
4 |
5 | ```go
6 | x := 42
7 | // "x" is the name of a location in memory. That location is storing the integer value of 42
8 | ```
9 |
10 | ## A Pointer is a Variable
11 |
12 | A pointer is a variable that stores the *memory address* of another variable. This means that a pointer "points to" the *location* of where the data is stored *NOT* the actual data itself.
13 |
14 | The `*` syntax defines a pointer:
15 |
16 | ```go
17 | var p *int
18 | ```
19 |
20 | The `&` operator generates a pointer to its operand.
21 |
22 | ```go
23 | myString := "hello"
24 | myStringPtr = &myString
25 | ```
26 |
27 | ## Why are Pointers Useful?
28 |
29 | Pointers allow us to manipulate data in memory directly, without making copies or duplicating data. This can make programs more efficient and allow us to do things that would be difficult or impossible without them.
30 |
31 | > That said, pointers can be *very* dangerous. It's generally a better idea to have your functions accept non-pointers and return new values rather than mutating pointer inputs.
32 |
33 | ## Nil Pointers
34 |
35 | Pointers can be very dangerous. If a pointer points to nothing (the zero value of the pointer type) then dereferencing it will cause a runtime error (a [panic](https://gobyexample.com/panic)) that crashes the program. Generally speaking, whenever you're dealing with pointers you should check if it's `nil` before trying to dereference it.
--------------------------------------------------------------------------------
/src/data/advanced-functions/why-higher-order-functions.md:
--------------------------------------------------------------------------------
1 | # Why First Class and Higher Order Functions?
2 |
3 | At first, it may seem like dynamically creating functions and passing them around as variables adds unnecessary complexity. Most of the time you would be right. There are cases however when functions as values make a lot of sense. Some of these include:
4 |
5 | - [HTTP API](https://en.wikipedia.org/wiki/Web_API) handlers
6 | - [Pub/Sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) handlers
7 | - Onclick callbacks
8 |
9 | Any time you need to run custom code at *a time in the future*, functions as values might make sense.
10 |
11 | ## Definition: First-class Functions
12 |
13 | A first-class function is a function that can be treated like any other value. Go supports first-class functions. A function's type is dependent on the types of its parameters and return values. For example, these are different function types:
14 |
15 | ```go
16 | func() int
17 | ```
18 |
19 | ```go
20 | func(string) int
21 | ```
22 |
23 | ## Definition: Higher-Order Functions
24 |
25 | A higher-order function is a function that takes a function as an argument or returns a function as a return value. Go supports higher-order functions. For example, this function takes a function as an argument:
26 |
27 | ```go
28 | func aggregate(a, b, c int, arithmetic func(int, int) int) int
29 | ```
30 |
--------------------------------------------------------------------------------
/src/data/advanced-functions/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Advanced Function Techniques in Go
4 |
5 | Go, while known for its simplicity, offers powerful capabilities for manipulating and leveraging functions in advanced ways.
6 |
7 | ### High-Order Functions: Taking Functions as Arguments
8 |
9 | High-order functions are functions that can accept other functions as arguments or return functions as results. This allows you to create reusable and flexible code structures.
10 |
11 | ```go
12 | func apply(f func(int) int, x int) int {
13 | return f(x)
14 | }
15 |
16 | func square(x int) int {
17 | return x * x
18 | }
19 |
20 | result := apply(square, 5) // Pass the "square" function as an argument
21 | fmt.Println(result) // Output: 25
22 | ```
23 |
24 | ### First-Class Functions: Treating Functions as Data
25 |
26 | In Go, functions are first-class citizens, meaning they can be treated like any other data type. You can assign functions to variables, pass them as arguments, and return them from other functions.
27 |
28 | ```go
29 | func getGreeting(name string) func() string {
30 | return func() string {
31 | return "Hello, " + name + "!"
32 | }
33 | }
34 |
35 | greeting := getGreeting("Alice")
36 | message := greeting() // Call the returned function
37 | fmt.Println(message) // Output: Hello, Alice!
38 | ```
39 |
40 | ### Currying: Partially Applying Functions
41 |
42 | Currying is a technique that involves transforming a function that takes multiple arguments into a series of nested functions, each taking a single argument.
43 |
44 | ```go
45 | func add(x int) func(int) int {
46 | return func(y int) int {
47 | return x + y
48 | }
49 | }
50 |
51 | add5 := add(5) // Partially apply the "add" function
52 | result := add5(10) // Apply the remaining argument
53 | fmt.Println(result) // Output: 15
54 | ```
55 |
56 | ### `defer` Statements: Scheduling Code for Later
57 |
58 | The `defer` statement schedules a function call to be executed after the surrounding function returns. This is useful for tasks like closing files, releasing resources, or performing cleanup actions.
59 |
60 | ```go
61 | func readFile(filename string) {
62 | file, err := os.Open(filename)
63 | if err != nil {
64 | // Handle the error
65 | }
66 | defer file.Close() // Close the file when the function returns
67 | // ... process the file content ...
68 | }
69 | ```
70 |
71 | ### Closures: Capturing Variables from the Enclosing Scope
72 |
73 | Closures allow functions to access variables from their surrounding scope, even after the outer function has returned. This enables you to create functions with state.
74 |
75 | ```go
76 | func makeCounter() func() int {
77 | count := 0
78 | return func() int {
79 | count++
80 | return count
81 | }
82 | }
83 |
84 | counter := makeCounter()
85 | fmt.Println(counter()) // Output: 1
86 | fmt.Println(counter()) // Output: 2
87 | ```
88 |
89 | ### Anonymous Functions: Functions Without Names
90 |
91 | Anonymous functions are functions that are not declared with a name. They are often used as arguments to other functions or returned as results.
92 |
93 | ```go
94 | func apply(f func(int) int, x int) int {
95 | return f(x)
96 | }
97 |
98 | result := apply(func(x int) int { return x * 2 }, 5) // Anonymous function as argument
99 | fmt.Println(result) // Output: 10
100 | ```
101 |
102 | ### Pointers: Referencing Memory Addresses
103 |
104 | Pointers store the memory addresses of other variables. This allows you to modify the values of variables directly, without copying them.
105 |
106 | ```go
107 | var x int = 10
108 | var p *int = &x // "p" points to the memory address of "x"
109 | *p = 20 // Modifies the value of "x" through the pointer
110 | fmt.Println(x) // Output: 20
111 | ```
112 |
113 | ### Pointer Receivers: Modifying Values Directly
114 |
115 | Methods can have pointer receivers, allowing them to modify the values of the underlying object directly, without creating a copy.
116 |
117 | ```go
118 | type Counter struct {
119 | count int
120 | }
121 |
122 | func (c *Counter) Increment() {
123 | c.count++
124 | }
125 |
126 | counter := Counter{0}
127 | counter.Increment() // Increment the counter's value
128 | fmt.Println(counter.count) // Output: 1
129 | ```
130 |
131 | You've explored a range of advanced function techniques in Go!
132 |
--------------------------------------------------------------------------------
/src/data/channels/channels-in-go.md:
--------------------------------------------------------------------------------
1 | # Channels
2 |
3 | Channels are a typed, thread-safe queue. Channels allow different goroutines to communicate with each other.
4 |
5 | ## Create a channel
6 |
7 | Like maps and slices, channels must be created *before* use. They also use the same `make` keyword:
8 |
9 | ```go
10 | ch := make(chan int)
11 | ```
12 |
13 | ## Send data to a channel
14 |
15 | ```go
16 | ch <- 69
17 | ```
18 |
19 | The `<-` operator is called the *channel operator*. Data flows in the direction of the arrow. This operation will *block* until another goroutine is ready to receive the value.
20 |
21 | ## Receive data from a channel
22 |
23 | ```go
24 | v := <-ch
25 | ```
26 |
27 | This reads and removes a value from the channel and saves it into the variable `v`. This operation will *block* until there is a value in the channel to be read.
28 |
29 | ## Blocking and deadlocks
30 |
31 | A [deadlock](https://yourbasic.org/golang/detect-deadlock/#:~:text=yourbasic.org%2Fgolang,look%20at%20this%20simple%20example.) is when a group of goroutines are all blocking so none of them can continue. This is a common bug that you need to watch out for in concurrent programming.
32 |
33 | > Empty structs are often used as `tokens` in Go programs. In this context, a token is a [unary](https://en.wikipedia.org/wiki/Unary_operation) value. In other words, we don't care *what* is passed through the channel. We care *when* and *if* it is passed.
34 |
35 | We can block and wait until *something* is sent on a channel using the following syntax
36 |
37 | ```go
38 | <-ch
39 | ```
40 |
41 | This will block until it pops a single item off the channel, then continue, discarding the item.
42 |
--------------------------------------------------------------------------------
/src/data/channels/channels-review.md:
--------------------------------------------------------------------------------
1 | # Channels Review
2 |
3 | Here are a few extra things you should understand about channels from [Dave Cheney's awesome article](https://dave.cheney.net/2014/03/19/channel-axioms).
4 |
5 | ### A send to a nil channel blocks forever
6 |
7 | ```go
8 | var c chan string // c is nil
9 | c <- "let's get started" // blocks
10 | ```
11 |
12 | ### A receive from a nil channel blocks forever
13 |
14 | ```go
15 | var c chan string // c is nil
16 | fmt.Println(<-c) // blocks
17 | ```
18 |
19 | ### A send to a closed channel panics
20 |
21 | ```go
22 | var c = make(chan int, 100)
23 | close(c)
24 | c <- 1 // panic: send on closed channel
25 | ```
26 |
27 | ### A receive from a closed channel returns the zero value immediately
28 |
29 | ```go
30 | var c = make(chan int, 100)
31 | close(c)
32 | fmt.Println(<-c) // 0
33 | ```
34 |
--------------------------------------------------------------------------------
/src/data/channels/closing-channels.md:
--------------------------------------------------------------------------------
1 | # Closing channels in Go
2 |
3 | Channels can be explicitly closed by a *sender*:
4 |
5 | ```go
6 | ch := make(chan int)
7 |
8 | // do some stuff with the channel
9 |
10 | close(ch)
11 | ```
12 |
13 | ## Checking if a channel is closed
14 |
15 | Similar to the `ok` value when accessing data in a `map`, receivers can check the `ok` value when receiving from a channel to test if a channel was closed.
16 |
17 | ```go
18 | v, ok := <-ch
19 | ```
20 |
21 | ok is `false` if the channel is empty and closed.
22 |
23 | ## Don't send on a closed channel
24 |
25 | Sending on a closed channel will cause a panic. A panic on the main goroutine will cause the entire program to crash, and a panic in any other goroutine will cause *that goroutine* to crash.
26 |
27 | Closing isn't necessary. There's nothing wrong with leaving channels open, they'll still be garbage collected if they're unused. You should close channels to indicate explicitly to a receiver that nothing else is going to come across.
28 |
29 | ## Buffered Channels
30 |
31 | Channels can *optionally* be buffered.
32 |
33 | ### Creating a channel with a buffer
34 |
35 | You can provide a buffer length as the second argument to `make()` to create a buffered channel:
36 |
37 | ```go
38 | ch := make(chan int, 100)
39 | ```
40 |
41 | Sending on a buffered channel only blocks when the buffer is *full*.
42 |
43 | Receiving blocks only when the buffer is *empty*.
44 |
--------------------------------------------------------------------------------
/src/data/channels/concurrency.md:
--------------------------------------------------------------------------------
1 | # Concurrency
2 |
3 | ## What is concurrency?
4 |
5 | Concurrency is the ability to perform multiple tasks at the same time. Typically, our code is executed one line at a time, one after the other. This is called *sequential execution* or *synchronous execution*.
6 |
7 | 
8 |
9 | If the computer we're running our code on has multiple cores, we can even execute multiple tasks at *exactly* the same time. If we're running on a single core, a single code executes code at *almost* the same time by switching between tasks very quickly. Either way, the code we write looks the same in Go and takes advantage of whatever resources are available.
10 |
11 | ## How does concurrency work in Go?
12 |
13 | Go was designed to be concurrent, which is a trait *fairly* unique to Go. It excels at performing many tasks simultaneously safely using a simple syntax.
14 |
15 | There isn't a popular programming language in existence where spawning concurrent execution is quite as elegant, at least in my opinion.
16 |
17 | Concurrency is as simple as using the `go` keyword when calling a function:
18 |
19 | ```go
20 | go doSomething()
21 | ```
22 |
23 | In the example above, `doSomething()` will be executed concurrently with the rest of the code in the function. The `go` keyword is used to spawn a new *[goroutine](https://gobyexample.com/goroutines)*.
24 |
--------------------------------------------------------------------------------
/src/data/channels/select-default-case.md:
--------------------------------------------------------------------------------
1 | # Select Default Case
2 |
3 | The `default` case in a `select` statement executes *immediately* if no other channel has a value ready. A `default` case stops the `select` statement from blocking.
4 |
5 | ```go
6 | select {
7 | case v := <-ch:
8 | // use v
9 | default:
10 | // receiving from ch would block
11 | // so do something else
12 | }
13 | ```
14 |
15 | ## Tickers
16 |
17 | - [time.Tick()](https://golang.org/pkg/time/#Tick) - is a standard library function that returns a channel that sends a value on a given interval.
18 | - [time.After()](https://golang.org/pkg/time/#After) - sends a value once after the duration has passed.
19 | - [time.Sleep()](https://golang.org/pkg/time/#Sleep) - blocks the current goroutine for the specified amount of time.
20 |
21 | ## Read-only Channels
22 |
23 | A channel can be marked as read-only by casting it from a `chan` to a `<-chan` type. For example:
24 |
25 | ```go
26 | func main(){
27 | ch := make(chan int)
28 | readCh(ch)
29 | }
30 |
31 | func readCh(ch <-chan int) {
32 | // ch can only be read from
33 | // in this function
34 | }
35 | ```
36 |
37 | ## Write-only Channels
38 |
39 | The same goes for write-only channels, but the arrow's position moves.
40 |
41 | ```go
42 | func writeCh(ch chan<- int) {
43 | // ch can only be written to
44 | // in this function
45 | }
46 | ```
47 |
--------------------------------------------------------------------------------
/src/data/channels/select-statement.md:
--------------------------------------------------------------------------------
1 | # Select
2 |
3 | Sometimes we have a single goroutine listening to multiple channels and want to process data in the order it comes through each channel.
4 |
5 | A `select` statement is used to listen to multiple channels at the same time. It is similar to a `switch` statement but for channels.
6 |
7 | ```go
8 | select {
9 | case i, ok := <- chInts:
10 | fmt.Println(i)
11 | case s, ok := <- chStrings:
12 | fmt.Println(s)
13 | }
14 | ```
15 |
16 | The first channel with a value ready to be received will fire and its body will execute. If multiple channels are ready at the same time one is chosen randomly. The `ok` variable in the example above refers to whether or not the channel has been closed by the sender yet.
17 |
18 | ## Range
19 |
20 | Similar to slices and maps, channels can be ranged over.
21 |
22 | ```go
23 | for item := range ch {
24 | // item is the next value received from the channel
25 | }
26 | ```
27 |
28 | This example will receive values over the channel (blocking at each iteration if nothing new is there) and will exit only when the channel is closed.
29 |
--------------------------------------------------------------------------------
/src/data/channels/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Concurrency
4 |
5 | Concurrency is the ability to handle multiple tasks seemingly simultaneously and a powerful paradigm for writing efficient and responsive programs. Go, with its built-in support for concurrency, makes it effortless to harness this power through its elegant channel mechanism.
6 |
7 | ### Concurrency in Go: Embracing Parallelism
8 |
9 | Go's concurrency model is based on the concept of goroutines, lightweight threads managed by the Go runtime, and channels, typed communication pipelines for goroutines. This allows you to run multiple functions concurrently, exchanging data between them seamlessly.
10 |
11 | ### Channels: Typed Communication Pipes
12 |
13 | Channels are typed conduits for passing data between goroutines. They ensure safe and synchronized communication, preventing race conditions.
14 |
15 | ```go
16 | ch := make(chan int) // Create a channel of integers
17 | ```
18 |
19 | ```go
20 | // Sending Data
21 | ch <- 5 // Send the value 5 to the channel
22 |
23 | // Receiving Data
24 | value := <-ch // Receive a value from the channel
25 | ```
26 |
27 | ### The `select` Keyword: Multi-Way Channel Communication
28 |
29 | The `select` statement provides a powerful way to handle multiple channel operations concurrently. It allows you to choose the first channel operation that becomes ready, elegantly handling multiple communication paths.
30 |
31 | ```go
32 | select {
33 | case value := <-ch1:
34 | fmt.Println("Received from ch1:", value)
35 | case ch2 <- 10:
36 | fmt.Println("Sent to ch2: 10")
37 | default:
38 | fmt.Println("No channels ready")
39 | }
40 | ```
41 |
42 | ### Channel Directions: Specifying Input and Output
43 |
44 | You can specify the direction of a channel using the `<-` operator in its declaration:
45 |
46 | Send-only channel:
47 |
48 | ```go
49 | ch := make(chan<- int) // Can only send data
50 | ```
51 |
52 | Receive-only channel:
53 |
54 | ```go
55 | ch := make(<-chan int) // Can only receive data
56 | ```
57 |
58 | ### Unbuffered Channels: Synchronous Communication
59 |
60 | Unbuffered channels require a sender and receiver to be ready simultaneously for data to be transferred. This provides a form of synchronization.
61 |
62 | ```go
63 | ch := make(chan int) // Unbuffered channel
64 | ch <- 5 // Sender waits until a receiver is ready
65 | value := <-ch // Receiver waits until a sender is ready
66 | ```
67 |
68 | ### Buffered Channels: Asynchronous Communication
69 |
70 | Buffered channels allow a certain number of values to be queued before a receiver is ready. This enables more asynchronous communication.
71 |
72 | ```go
73 | ch := make(chan int, 2) // Buffered channel with capacity 2
74 | ch <- 5 // Send a value without waiting
75 | ch <- 10 // Send another value without waiting
76 | value := <-ch // Receive the first value
77 | ```
78 |
79 | ### Closing Channels: Signaling Completion
80 |
81 | The `close` function signals that a channel is no longer being used. Receivers can detect a closed channel and handle the situation accordingly.
82 |
83 | ```go
84 | close(ch) // Close the channel
85 | value, ok := <-ch // Receive from the closed channel
86 | if ok {
87 | // Process the value
88 | } else {
89 | // Handle the case where the channel is closed
90 | }
91 | ```
92 |
--------------------------------------------------------------------------------
/src/data/errors/custom-errors.md:
--------------------------------------------------------------------------------
1 | # Custom Errors
2 |
3 | Because errors are just interfaces, you can build your own custom types that implement the `error` interface. Here's an example of a `userError` struct that implements the `error` interface:
4 |
5 | ```go
6 | type userError struct {
7 | name string
8 | }
9 |
10 | func (e userError) Error() string {
11 | return fmt.Sprintf("%v has a problem with their account", e.name)
12 | }
13 | ```
14 |
15 | It can then be used as an error:
16 |
17 | ```go
18 | func sendSMS(msg, userName string) error {
19 | if !canSendToUser(userName) {
20 | return userError{name: userName}
21 | }
22 | ...
23 | }
24 | ```
25 |
--------------------------------------------------------------------------------
/src/data/errors/formatting-strings.md:
--------------------------------------------------------------------------------
1 | # Formatting Strings Review
2 |
3 | A convenient way to format strings in Go is by using the standard library's [fmt.Sprintf()](https://pkg.go.dev/fmt#example-Sprintf) function. It's a string interpolation function, similar to JavaScript's built-in template literals. The `%v` substring uses the type's default formatting, which is often what you want.
4 |
5 | ## Default Values
6 |
7 | ```go
8 | const name = "Kim"
9 | const age = 22
10 | s := fmt.Sprintf("%v is %v years old.", name, age)
11 | // s = "Kim is 22 years old."
12 | ```
13 |
14 | The equivalent JavaScript code:
15 |
16 | ```js
17 | const name = 'Kim'
18 | const age = 22
19 | s = `${name} is ${age} years old.`
20 | // s = "Kim is 22 years old."
21 | ```
22 |
23 | ## Rounding floats
24 |
25 | ```go
26 | fmt.Printf("I am %f years old", 10.523)
27 | // I am 10.523000 years old
28 |
29 | // The ".2" rounds the number to 2 decimal places
30 | fmt.Printf("I am %.2f years old", 10.523)
31 | // I am 10.53 years old
32 | ```
33 |
--------------------------------------------------------------------------------
/src/data/errors/the-error-interface.md:
--------------------------------------------------------------------------------
1 | # The Error Interface
2 |
3 | Go programs express errors with `error` values. An Error is any type that implements the simple built-in [error interface](https://blog.golang.org/error-handling-and-go):
4 |
5 | ```go
6 | type error interface {
7 | Error() string
8 | }
9 | ```
10 |
11 | When something can go wrong in a function, that function should return an `error` as its last return value. Any code that calls a function that can return an `error` should handle errors by testing whether the error is `nil`.
12 |
13 | ```go
14 | // Atoi converts a stringified number to an interger
15 | i, err := strconv.Atoi("42b")
16 | if err != nil {
17 | fmt.Println("couldn't convert:", err)
18 | // because "42b" isn't a valid integer, we print: couldn't convert: strconv.Atoi: parsing "42b": invalid syntax
19 | // Note:
20 | // 'parsing "42b": invalid syntax' is returned by the .Error() method
21 | return
22 | }
23 | // if we get here, then i was converted successfully
24 | ```
25 |
26 | A `nil` error denotes success; a non-nil error denotes failure.
27 |
28 | > When you return a non-nil error in Go, it's conventional to return the "zero" values of all other return values.
29 |
--------------------------------------------------------------------------------
/src/data/errors/the-errors-package.md:
--------------------------------------------------------------------------------
1 | # The Errors Package
2 |
3 | The Go standard library provides an "errors" package that makes it easy to deal with errors. Read the godoc for the [errors.New()](https://pkg.go.dev/errors#New) function, but here's a simple example:
4 |
5 | ```go
6 | var err error = errors.New("something went wrong")
7 | ```
8 |
9 | ### Remember
10 |
11 | Go programs express errors with `error` values. Error-values are any type that implements the simple built-in [error interface](https://blog.golang.org/error-handling-and-go). Keep in mind that the way Go handles errors is fairly unique. Most languages treat errors as something special and different. For example, Python raises exception types and JavaScript throws and catches errors. In Go, an `error` is just another value that we handle like any other value - however, we want! There aren't any special keywords for dealing with them.
12 |
--------------------------------------------------------------------------------
/src/data/errors/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Handling Errors Gracefully
4 |
5 | Errors are an inevitable part of programming. Go provides a robust and elegant system for handling errors, making your code more reliable and resilient.
6 |
7 | ### The `error` Interface: A Standard for Error Handling
8 |
9 | At the heart of Go's error handling system lies the `error` interface. This interface defines a single method, `Error()`, which returns a string describing the error:
10 |
11 | ```go
12 | type error interface {
13 | Error() string
14 | }
15 | ```
16 |
17 | Any type that implements the `Error()` method satisfies the `error` interface, making it a standard way to represent errors.
18 |
19 | ### Returning Errors: Signalling Problems
20 |
21 | Functions that might encounter errors typically return an `error` value. This signals to the caller that something went wrong.
22 |
23 | ```go
24 | func divide(a, b int) (int, error) {
25 | if b == 0 {
26 | return 0, errors.New("division by zero") // Return an error if b is zero
27 | }
28 | return a / b, nil // Return the result and nil (no error) if successful
29 | }
30 | ```
31 |
32 | ### Checking for Errors: Detecting and Handling Problems
33 |
34 | The caller of a function must check for errors. You typically use an `if` statement to check if the error is `nil`, indicating success:
35 |
36 | ```go
37 | result, err := divide(10, 0) // Call the "divide" function
38 | if err != nil {
39 | fmt.Println("Error:", err) // Handle the error if it's not nil
40 | } else {
41 | fmt.Println("Result:", result) // Use the result if there's no error
42 | }
43 | ```
44 |
45 | ### Custom Errors: Providing More Context
46 |
47 | Go's built-in `errors.New` function is useful for creating basic errors, but you can create custom errors to provide more context and information.
48 |
49 | ```go
50 | type InsufficientFundsError struct {
51 | Balance int
52 | Amount int
53 | }
54 |
55 | func (e InsufficientFundsError) Error() string {
56 | return fmt.Sprintf("Insufficient funds: balance is %d, requested amount is %d", e.Balance, e.Amount)
57 | }
58 |
59 | func withdraw(balance int, amount int) (int, error) {
60 | if balance < amount {
61 | return 0, InsufficientFundsError{Balance: balance, Amount: amount} // Return a custom error
62 | }
63 | return balance - amount, nil
64 | }
65 | ```
66 |
67 | ### The `errors` Package: Useful Error Handling Utilities
68 |
69 | Go's `errors` package provides helpful functions for working with errors, such as:
70 |
71 | - `errors.New`: Creates a basic error.
72 | - `errors.Wrap`: Wraps an existing error with additional context.
73 | - `errors.Unwrap`: Extracts the underlying error from a wrapped error.
74 |
75 | ### Formatting Error Strings: Clear and Descriptive Messages
76 |
77 | Use `fmt.Errorf` or `fmt.Sprintf` to create error strings that are clear, concise, and informative. Include relevant details, such as the operation that failed or the specific values involved.
78 |
79 | ```go
80 | func openFile(filename string) (io.Reader, error) {
81 | file, err := os.Open(filename)
82 | if err != nil {
83 | return nil, fmt.Errorf("failed to open file %s: %w", filename, err) // Wrap the error with context
84 | }
85 | return file, nil
86 | }
87 | ```
88 |
89 | You've now explored the core concepts of error handling in Go!
90 |
--------------------------------------------------------------------------------
/src/data/extras/clean-package.md:
--------------------------------------------------------------------------------
1 | # Clean Package
2 |
3 | Learning to properly build small and reusable packages can take your Go career to the next level.
4 |
5 | ## Rules Of Thumb
6 |
7 | ### 1. Hide internal logic
8 |
9 | If you're familiar with the pillars of OOP, this is a practice in *encapsulation*.
10 |
11 | Oftentimes an application will have complex logic that requires a lot of code. In almost every case the logic that the application cares about can be exposed via an API, and most of the dirty work can be kept within a package. For example, imagine we are building an application that needs to classify images. We could build a package:
12 |
13 | ```go
14 | package classifier
15 |
16 | // ClassifyImage classifies images as "hotdog" or "not hotdog"
17 | func ClassifyImage(image []byte) (imageType string) {
18 | return hasHotdogColors(image) && hasHotdogShape(image)
19 | }
20 |
21 | func hasHotdogShape(image []byte) bool {
22 | // internal logic that the application doesn't need to know about
23 | return true
24 | }
25 |
26 | func hasHotdogColors(image []byte) bool {
27 | // internal logic that the application doesn't need to know about
28 | return true
29 | }
30 | ```
31 |
32 | We create an API by only exposing the function(s) that the application-level needs to know about. All other logic is unexported to keep a clean separation of concerns. The application doesn’t need to know how to classify an image, just the result of the classification.
33 |
34 | ### 2. Don’t change APIs
35 |
36 | The unexported functions within a package can and should change often for testing, refactoring, and bug fixing.
37 |
38 | A well-designed library will have a stable API so that users aren’t receiving breaking changes each time they update the package version. In Go, this means not changing exported function’s signatures.
39 |
40 | ### 3. Don’t export functions from the main package
41 |
42 | A `main` package isn't a library, there's no need to export functions from it.
43 |
44 | ### 4. Packages shouldn't know about dependents
45 |
46 | Perhaps one of the most important and most broken rules is that a package shouldn’t know anything about its dependents. In other words, a package should never have specific knowledge about a particular application that uses it.
47 |
--------------------------------------------------------------------------------
/src/data/extras/custom-package.md:
--------------------------------------------------------------------------------
1 | # Custom Package
2 |
3 | Let's write a package to import and use in our project.
4 |
5 | First navigate to a folder where we created [our first Go program](/setting-up-environment/your-first-go-program). Then create a sibling directory at the same level as the `hellogo` directory:
6 |
7 | ```bash
8 | mkdir mystrings
9 | cd mystrings
10 | ```
11 |
12 | Initialize a [module](/setting-up-environment/modules):
13 |
14 | ```bash
15 | go mod init {REMOTE}/{USERNAME}/mystrings
16 | ```
17 |
18 | Then create a new file `mystrings.go` in that directory and paste the following code:
19 |
20 | ```go
21 | // by convention, we name our package the same as the directory
22 | package mystrings
23 |
24 | // Reverse reverses a string left to right
25 | // Notice that we need to capitlize the first letter of the function
26 | // If we don't then we won't be able access this function outside of the
27 | // mystrings package
28 | func Reverse(s string) string {
29 | result := ""
30 | for _, v := range s {
31 | result = string(v) + result
32 | }
33 | return result
34 | }
35 | ```
36 |
37 | Note that there is no `main.go` or `func main()` in this package.
38 |
39 | `go build` won't build an executable from a library package. However, `go build` will still compile the package and save it to our local build cache. It's useful for checking for compile errors.
40 |
41 | Run:
42 |
43 | ```bash
44 | go build
45 | ```
46 |
47 | ## Using the custom package
48 |
49 | Let's use our new `mystrings` package in `hellogo`
50 |
51 | Modify hellogo's `main.go` file:
52 |
53 | ```go
54 | package main
55 |
56 | import (
57 | "fmt"
58 |
59 | "{REMOTE}/{USERNAME}/mystrings"
60 | )
61 |
62 | func main() {
63 | fmt.Println(mystrings.Reverse("hello world"))
64 | }
65 | ```
66 |
67 | Don't forget to replace {REMOTE} and {USERNAME} with the values you used before. Then edit hellogo's `go.mod` file to contain the following:
68 |
69 | ```go
70 | module example.com/username/hellogo
71 |
72 | go 1.20
73 |
74 | replace example.com/username/mystrings v0.0.0 => ../mystrings
75 |
76 | require (
77 | example.com/username/mystrings v0.0.0
78 | )
79 | ```
80 |
81 | Now build and run the new program:
82 |
83 | ```bash
84 | go build
85 | ./hellogo
86 | ```
87 |
--------------------------------------------------------------------------------
/src/data/extras/proverbs.md:
--------------------------------------------------------------------------------
1 | # Go Proverbs
2 |
3 | Similar to the [Zen of Python](https://peps.python.org/pep-0020/), the [Go Proverbs](https://go-proverbs.github.io/) are a beautiful collection of wise words from Rob Pike, one of Go's creators.
4 |
5 | - Don't communicate by sharing memory, share memory by communicating.
6 | - Concurrency is not parallelism.
7 | - Channels orchestrate; mutexes serialize.
8 | - The bigger the interface, the weaker the abstraction.
9 | - Make the zero value useful.
10 | - interface{} says nothing.
11 | - Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.
12 | - A little copying is better than a little dependency.
13 | - Syscall must always be guarded with build tags.
14 | - Cgo must always be guarded with build tags.
15 | - Cgo is not Go.
16 | - With the unsafe package there are no guarantees.
17 | - Clear is better than clever.
18 | - Reflection is never clear.
19 | - Errors are values.
20 | - Don't just check errors, handle them gracefully.
21 | - Design the architecture, name the components, document the details.
22 | - Documentation is for users.
23 | - Don't panic.
24 |
--------------------------------------------------------------------------------
/src/data/extras/remote-package.md:
--------------------------------------------------------------------------------
1 | # Remote Package
2 |
3 | Imagine you're building a Go application and you need to perform complex tasks like working with databases, sending emails, or generating PDFs. Building all of these functionalities from scratch would be a monumental task. That's where Go's remote package management comes in, saving you time and effort by providing a vast ecosystem of pre-built, ready-to-use packages.
4 |
5 | ### 1. Go Modules: The Foundation for Package Management
6 |
7 | Go Modules are the official package management system introduced in Go 1.11. They provide a centralized way to define, share, and consume packages, streamlining the development process.
8 |
9 | - `go.mod`: The heart of every Go module is the `go.mod` file. This file acts as a manifest, declaring the module's name, version, and the dependencies it requires.
10 | - `go get`: This command is your primary tool for fetching and installing packages from remote repositories.
11 |
12 | ### 2. Fetching Packages: The `go get` Command
13 |
14 | Let's say you need to work with JSON data in your project. The `encoding/json` package is already part of the standard library, so no need to fetch it. But if you want to use a popular third-party package like `github.com/spf13/cobra` for command-line interface (CLI) building, you'll use `go get`.
15 |
16 | ```go
17 | go get github.com/spf13/cobra
18 | ```
19 |
20 | This command will:
21 |
22 | 1. Download the package: It fetches the code from the specified Git repository.
23 | 2. Install the package: It places the package in your Go workspace.
24 | 3. Update `go.mod`: It adds the package as a dependency in your `go.mod` file.
25 |
26 | ### 3. Using Packages: Including and Importing
27 |
28 | Once a package is installed, you can use it in your code by importing it.
29 |
30 | ```go
31 | package main
32 |
33 | import (
34 | "fmt"
35 | "github.com/spf13/cobra" // Import the cobra package
36 | )
37 |
38 | func main() {
39 | var rootCmd = &cobra.Command{
40 | Use: "my-cli",
41 | Short: "A simple CLI application",
42 | }
43 |
44 | rootCmd.AddCommand(
45 | // Add subcommands to your CLI here...
46 | )
47 |
48 | if err := rootCmd.Execute(); err != nil {
49 | fmt.Println(err)
50 | }
51 | }
52 | ```
53 |
54 | **Code breakdown**:
55 |
56 | - `import "github.com/spf13/cobra"`: This line imports the `cobra` package, making its functions and types available for use in your code.
57 | - `&cobra.Command{}`: This creates a new `cobra.Command` object, which represents your CLI application.
58 | - `rootCmd.Execute()`: This starts the CLI and executes its commands.
59 |
60 | ### 4. Managing Dependencies: `go mod tidy`
61 |
62 | As your project grows, you may add more packages. The `go mod tidy` command keeps your `go.mod` file clean and efficient:
63 |
64 | - Removes unused packages: If a package is no longer used, `go mod tidy` removes it from `go.mod`.
65 | - Adds missing dependencies: If your code requires a package that's not listed in `go.mod`, `go mod tidy` adds it automatically.
66 |
67 | ### 5. Version Control: `go get -u`
68 |
69 | You can specify a specific version of a package using semantic versioning (e.g., `v1.2.3`).
70 |
71 | ```go
72 | go get github.com/spf13/cobra@v1.2.3
73 | ```
74 |
75 | To update a package to its latest version:
76 |
77 | ```go
78 | go get -u github.com/spf13/cobra
79 | ```
80 |
81 | ### The Power of a Rich Ecosystem
82 |
83 | - Explore the Go Package Index (GOPROXY): You can browse available packages and their documentation at [https://pkg.go.dev/](https://pkg.go.dev/).
84 | - Contribute to the Community: Consider creating your own Go packages and sharing them with others on GitHub.
85 |
--------------------------------------------------------------------------------
/src/data/extras/standard-library.md:
--------------------------------------------------------------------------------
1 | # Standard Library
2 |
3 | The Go standard library is a developer's dream, offering a plethora of pre-built tools and packages to handle just about any task you can imagine.
4 |
5 | Let's dive into some of the most common and useful packages within the standard library.
6 |
7 | ### 1. `net/http`: Your Gateway to the Web
8 |
9 | The `net/http` package is your go-to for creating web servers and interacting with web resources.
10 |
11 | **Example: Building a Simple Web Server:**
12 |
13 | ```go
14 | package main
15 |
16 | import (
17 | "fmt"
18 | "net/http"
19 | )
20 |
21 | func handler(w http.ResponseWriter, r *http.Request) {
22 | fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
23 | }
24 |
25 | func main() {
26 | http.HandleFunc("/", handler) // Define a handler for the root path
27 | fmt.Println("Server listening on port 8080")
28 | http.ListenAndServe(":8080", nil) // Start the server
29 | }
30 | ```
31 |
32 | **Code breakdown:**
33 |
34 | - **`handler(w http.ResponseWriter, r *http.Request)`:** This function handles requests to the server. It writes a response to the `w` object, which represents the HTTP response. `r` holds the incoming request details.
35 | - **`http.HandleFunc("/", handler)`:** This line tells the server to use the `handler` function to respond to requests to the root path (`/`).
36 | - **`http.ListenAndServe(":8080", nil)`:** This starts the server, listening on port `8080`.
37 |
38 | Save this code as `server.go` and run it. Then open your browser and navigate to `http://localhost:8080/` (or any path you want). You'll see the message displayed by the `handler` function.
39 |
40 | ### 2. `os`: Interacting with the Operating System
41 |
42 | The `os` package gives you access to system-level functionalities like working with files, environment variables, processes, and more.
43 |
44 | **Example: Reading a File:**
45 |
46 | ```go
47 | package main
48 |
49 | import (
50 | "bufio"
51 | "fmt"
52 | "os"
53 | )
54 |
55 | func main() {
56 | file, err := os.Open("my_file.txt") // Open the file
57 | if err != nil {
58 | fmt.Println("Error opening file:", err)
59 | return
60 | }
61 | defer file.Close() // Close the file when the function exits
62 |
63 | scanner := bufio.NewScanner(file) // Create a scanner for the file
64 | for scanner.Scan() {
65 | line := scanner.Text()
66 | fmt.Println(line)
67 | }
68 |
69 | if err := scanner.Err(); err != nil {
70 | fmt.Println("Error reading file:", err)
71 | }
72 | }
73 | ```
74 |
75 | **Code breakdown:**
76 |
77 | - **`file, err := os.Open("my_file.txt")`:** This line opens the file named `my_file.txt`.
78 | - **`defer file.Close()`:** The `defer` keyword ensures that the file will be closed after the function finishes, even if an error occurs.
79 | - **`scanner := bufio.NewScanner(file)`:** This creates a scanner that reads the file line by line.
80 | - **`for scanner.Scan() { ... }`:** This loop reads each line and prints it to the console.
81 |
82 | Create a file named `my_file.txt` with some text in it. Then run the code and you'll see the content of the file printed to the console.
83 |
84 | ### 3. `time`: Time and Date Management
85 |
86 | The `time` package lets you work with dates, times, durations, and time zones.
87 |
88 | **Example: Getting Current Time:**
89 |
90 | ```go
91 | package main
92 |
93 | import (
94 | "fmt"
95 | "time"
96 | )
97 |
98 | func main() {
99 | now := time.Now() // Get the current time
100 | fmt.Println("Current time:", now)
101 |
102 | fmt.Println("Hour:", now.Hour())
103 | fmt.Println("Minute:", now.Minute())
104 | fmt.Println("Second:", now.Second())
105 | }
106 | ```
107 |
108 | **Code breakdown:**
109 |
110 | - **`now := time.Now()`:** Gets the current time and stores it in the `now` variable.
111 | - **`fmt.Println("Current time:", now)`:** Prints the current time in its full format.
112 | - **`now.Hour()`, `now.Minute()`, `now.Second()`:** Access specific time components.
113 |
114 | This code will print the current date and time along with its hour, minute, and second.
115 |
116 | ### The Power of the Standard Library
117 |
118 | These are just a few examples of the vast capabilities of the Go standard library. Explore the documentation [https://pkg.go.dev/](https://pkg.go.dev/) for a comprehensive list of packages and their functions.
119 |
--------------------------------------------------------------------------------
/src/data/extras/user-input.md:
--------------------------------------------------------------------------------
1 | # User Input
2 |
3 | Imagine your program is a fun chatbot, a helpful calculator, or even a game that needs players to participate. We need a way to let them feed information into our Go programs.
4 |
5 | Let's break down how to do this, piece by piece, using code that's as clear as a sunny day!
6 |
7 | ### 1. The "Scan" Approach: Simple and Straightforward
8 |
9 | The `fmt` package is a Go godsend, packed with tools for input and output. The `fmt.Scanln()` function is your go-to for getting user input. It reads a line from standard input (usually your keyboard) and stores it as a string.
10 |
11 | ```go
12 | package main
13 |
14 | import (
15 | "fmt"
16 | )
17 |
18 | func main() {
19 | fmt.Print("What's your name? ") // Ask the user for their name
20 | var name string
21 | fmt.Scanln(&name) // Read the input and store it in the "name" variable
22 |
23 | fmt.Println("Hello,", name, "!") // Greet the user
24 | }
25 | ```
26 |
27 | **Code breakdown:**
28 |
29 | - **`fmt.Print("What's your name? ")`:** This line displays a prompt on the screen, asking the user for their name.
30 | - **`var name string`:** We declare a variable `name` of type `string` to hold the user's input.
31 | - **`fmt.Scanln(&name)`:** This is the magic! `Scanln` reads the input from the user (until they press Enter) and stores it in the `name` variable. The ampersand `&` is crucial here. It gives `Scanln` the memory address of `name`, so it can directly modify the variable.
32 |
33 | When you execute this program, you'll see the prompt. Type your name and press Enter. The program will then greet you!
34 |
35 | ### 2. The "bufio" Approach: For the Big Leagues
36 |
37 | The `bufio` package lets you work with buffered I/O, which is super helpful when dealing with a lot of input. It's like having a "buffer" to store input before processing it, which can be a performance boost.
38 |
39 | ```go
40 | package main
41 |
42 | import (
43 | "bufio"
44 | "fmt"
45 | "os"
46 | )
47 |
48 | func main() {
49 | reader := bufio.NewReader(os.Stdin) // Create a new reader for standard input
50 |
51 | fmt.Print("Enter a number: ")
52 | input, _ := reader.ReadString('\n') // Read a line until newline is encountered
53 |
54 | // Convert the input to an integer
55 | number, err := fmt.Sscan(input, &number)
56 | if err != nil {
57 | fmt.Println("Invalid input!")
58 | return
59 | }
60 |
61 | fmt.Println("You entered:", number)
62 | }
63 | ```
64 |
65 | **Code breakdown:**
66 |
67 | - **`reader := bufio.NewReader(os.Stdin)`:** Creates a new reader associated with the standard input (your keyboard).
68 | - **`input, _ := reader.ReadString('\n')`:** Reads a line from the user until it encounters a newline character (`\n`). It stores the input in the `input` variable.
69 | - **`number, err := fmt.Sscan(input, &number)`:** This line converts the string input (`input`) into an integer and stores it in the `number` variable.
70 | - **`if err != nil { ... }`:** Error handling! If there's a problem with the conversion (like the user enters a non-numeric value), an error is returned, and the program handles it gracefully.
71 |
72 | Type in a number and hit Enter. The program will confirm your input!
73 |
74 | ### 3. User Input: It's Not Just Text
75 |
76 | So far, we've dealt with text input. But what if you want to get numbers, dates, or even custom types?
77 |
78 | Fear not, Go's got your back! We can use the `fmt.Scanf()` function to directly read formatted input.
79 |
80 | ```go
81 | package main
82 |
83 | import (
84 | "fmt"
85 | )
86 |
87 | func main() {
88 | var age int
89 | fmt.Print("How old are you? ")
90 | fmt.Scanf("%d", &age) // %d tells Scanf to expect an integer
91 |
92 | fmt.Println("You are", age, "years old!")
93 | }
94 | ```
95 |
96 | **Code breakdown:**
97 |
98 | - **`var age int`:** We declare an integer variable `age` to store the input.
99 | - **`fmt.Scanf("%d", &age)`:** This line is where the magic happens. `Scanf` reads input and tries to match it with the format specifier `%d` (for integer). If the input is a valid integer, it is stored in the `age` variable.
100 |
101 | Type in a number representing your age and hit Enter. The program will tell you how old you are!
102 |
--------------------------------------------------------------------------------
/src/data/functions/benefits-of-named-returns.md:
--------------------------------------------------------------------------------
1 | # The Benefits of Named Returns
2 |
3 | Named returns have a couple of benefits when it comes to writing idiomatic Go code.
4 |
5 | ## Good For Documentation (Understanding)
6 |
7 | Named return parameters are great for documenting a function. We know what the function is returning directly from its signature, no need for a comment. Named return parameters are particularly important in longer functions with many return values.
8 |
9 | ```go
10 | func calculator(a, b int) (mul, div int, err error) {
11 | if b == 0 {
12 | return 0, 0, errors.New("Can't divide by zero")
13 | }
14 | mul = a * b
15 | div = a / b
16 | return mul, div, nil
17 | }
18 | ```
19 |
20 | Which is easier to understand than:
21 |
22 | ```go
23 | func calculator(a, b int) (int, int, error) {
24 | if b == 0 {
25 | return 0, 0, errors.New("Can't divide by zero")
26 | }
27 | mul := a * b
28 | div := a / b
29 | return mul, div, nil
30 | }
31 | ```
32 |
33 | We know *the meaning* of each return value just by looking at the function signature: "`func calculator(a, b int) (mul, div int, err error)`"
34 |
35 | ## Less Code (Sometimes)
36 |
37 | If there are multiple return statements in a function, you don’t need to write all the return values each time, though you *probably* should. When you choose to omit return values, it's called a *naked* return. Naked returns should only be used in short and simple functions.
38 |
--------------------------------------------------------------------------------
/src/data/functions/declaration-syntax.md:
--------------------------------------------------------------------------------
1 | # Declaration Syntax
2 |
3 | Developers often wonder why the declaration syntax in Go is different from the tradition established in the C family of languages.
4 |
5 | ## C-Style Syntax
6 |
7 | The C language describes types with an expression including the name to be declared, and states what type that expression will have.
8 |
9 | ```c
10 | int y;
11 | ```
12 |
13 | The code above declares `y` as an `int`. In general, the type goes on the left and the expression on the right. Interestingly, the creators of the Go language agreed that the C-style of declaring types in signatures gets confusing really fast - take a look at this nightmare.
14 |
15 | ```c
16 | int (*fp)(int (*ff)(int x, int y), int b)
17 | ```
18 |
19 | ## Go-style Syntax
20 |
21 | Go's declarations are clear, you just read them left to right, just like you would in English.
22 |
23 | ```go
24 | x int
25 | p *int
26 | a [3]int
27 | ```
28 |
29 | It's nice for more complex signatures, it makes them easier to read.
30 |
31 | ```go
32 | f func(func(int,int) int, int) int
33 | ```
34 |
35 | ## Reference
36 |
37 | The [following post on the Go blog](https://blog.golang.org/declaration-syntax) is a great resource for further reading on declaration syntax.
38 |
--------------------------------------------------------------------------------
/src/data/functions/early-returns.md:
--------------------------------------------------------------------------------
1 | # Early Returns
2 |
3 | Go supports the ability to return early from a function. This is a powerful feature that can clean up code, especially when used as guard clauses. Guard Clauses leverage the ability to `return` early from a function (or `continue` through a loop) to make nested conditionals one-dimensional. Instead of using if/else chains, we just return early from the function at the end of each conditional block.
4 |
5 | ```go
6 | func divide(dividend, divisor int) (int, error) {
7 | if divisor == 0 {
8 | return 0, errors.New("Can't divide by zero")
9 | }
10 | return dividend/divisor, nil
11 | }
12 | ```
13 |
14 | Error handling in Go naturally encourages developers to make use of guard clauses. Let’s take a look at an exaggerated example of nested conditional logic:
15 |
16 | ```go
17 | func getInsuranceAmount(status insuranceStatus) int {
18 | amount := 0
19 | if !status.hasInsurance(){
20 | amount = 1
21 | } else {
22 | if status.isTotaled(){
23 | amount = 10000
24 | } else {
25 | if status.isDented(){
26 | amount = 160
27 | if status.isBigDent(){
28 | amount = 270
29 | }
30 | } else {
31 | amount = 0
32 | }
33 | }
34 | }
35 | return amount
36 | }
37 | ```
38 |
39 | This could be written with guard clauses instead:
40 |
41 | ```go
42 | func getInsuranceAmount(status insuranceStatus) int {
43 | if !status.hasInsurance(){
44 | return 1
45 | }
46 | if status.isTotaled(){
47 | return 10000
48 | }
49 | if !status.isDented(){
50 | return 0
51 | }
52 | if status.isBigDent(){
53 | return 270
54 | }
55 | return 160
56 | }
57 | ```
58 |
59 | The example above is *much* easier to read and understand. When writing code, it’s important to try to reduce the cognitive load on the reader by reducing the number of entities they need to think about at any given time. In the first example, if the developer is trying to figure out `when` 270 is returned, they need to think about each branch in the logic tree and try to remember which cases matter and which cases don’t. With the one-dimensional structure offered by guard clauses, it’s as simple as stepping through each case in order.
60 |
--------------------------------------------------------------------------------
/src/data/functions/intro.md:
--------------------------------------------------------------------------------
1 | # Functions Intro
2 |
3 | Functions in Go can take zero or more arguments. To make Go code easier to read, the variable type comes *after* the variable name. For example, the following function:
4 |
5 | ```go
6 | func sub(x int, y int) int {
7 | return x - y
8 | }
9 | ```
10 |
11 | Accepts two integer parameters and returns another integer. Here, "`func sub(x int, y int) int`" is known as the **function signature**.
12 |
13 | ## Multiple Parameters
14 |
15 | When multiple arguments are of the same type, the type only needs to be declared after the last one, assuming they are in order. For example:
16 |
17 | ```go
18 | func add(x, y int) int {
19 | return x + y
20 | }
21 | ```
22 |
23 | If they are not in order they need to be defined separately.
24 |
--------------------------------------------------------------------------------
/src/data/functions/named-returns.md:
--------------------------------------------------------------------------------
1 | # Named Return Values
2 |
3 | Return values may be given names, and if they are, then they are treated the same as if they were new variables defined at the top of the function. Named return values are best thought of as a way to document the purpose of the returned values. According to the [tour of go](https://tour.golang.org/):
4 |
5 | > A return statement without arguments returns the named return values. This is known as a "naked" return. Naked return statements should be used only in short functions. They can harm readability in longer functions.
6 |
7 | ```go
8 | func getCoords() (x, y int){
9 | // x and y are initialized with zero values
10 |
11 | return // automatically returns x and y
12 | }
13 | ```
14 |
15 | Is the same as:
16 |
17 | ```go
18 | func getCoords() (int, int){
19 | var x int
20 | var y int
21 | return x, y
22 | }
23 | ```
24 |
25 | In the first example, `x` and `y` are the return values. At the end of the function, we could simply write `return` to return the values of those two variables, rather that writing `return x,y`.
26 |
27 | ## Named Return Values - Implicit Returns
28 |
29 | Even though a function has named return values, we can still explicitly return values if we want to.
30 |
31 | ```go
32 | func getCoords() (x, y int){
33 | return x, y // this is explicit
34 | }
35 | ```
36 |
37 | Using this explicit pattern we can even overwrite the return values:
38 |
39 | ```go
40 | func getCoords() (x, y int){
41 | return 5, 6 // this is explicit, x and y are NOT returned
42 | }
43 | ```
44 |
45 | Otherwise, if we want to return the values defined in the function signature we can just use a naked `return` (blank return):
46 |
47 | ```go
48 | func getCoords() (x, y int){
49 | return // implicitly returns x and y
50 | }
51 | ```
52 |
--------------------------------------------------------------------------------
/src/data/functions/return-values.md:
--------------------------------------------------------------------------------
1 | # Return Values
2 |
3 | ## Passing Variables by Value
4 |
5 | Variables in Go are passed by value (except for a few data types we haven't covered yet). "Pass by value" means that when a variable is passed into a function, that function receives a *copy* of the variable. The function is unable to mutate the caller's data.
6 |
7 | ```go
8 | func main(){
9 | x := 5
10 | increment(x)
11 |
12 | fmt.Println(x)
13 | // still prints 5, because the increment function received a copy of x
14 | }
15 |
16 | func increment(x int){
17 | x++
18 | }
19 | ```
20 |
21 | ## Ignoring Return Values
22 |
23 | A function can return a value that the caller doesn't care about. We can explicitly ignore variables by using an underscore. For example:
24 |
25 | ```go
26 | func getPoint() (x int, y int) {
27 | return 3, 4
28 | }
29 |
30 | // ignore y value
31 | x, _ := getPoint()
32 | ```
33 |
34 | Even though `getPoint()` returns two values, we can capture the first one and ignore the second.
35 |
36 | ### Why would you ignore a return value?
37 |
38 | There could be many reasons. For example, maybe a function called `getCircle` returns the center point and the radius, but you really only need the radius for your calculation. In that case, you would ignore the center point variable. This is crucial to understand because the Go compiler will throw an error if you have unused variable declarations in your code, so you *need* to ignore anything you don't intend to use.
39 |
--------------------------------------------------------------------------------
/src/data/functions/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Functions in Go
4 |
5 | Functions are the heart of any programming language, and Go is no exception. They allow you to break down complex tasks into smaller, manageable units, making your code more organized, efficient, and reusable.
6 |
7 | ### Introduction to Functions: The Building Blocks of Code
8 |
9 | Imagine you need to perform a specific task repeatedly in your program, like calculating the area of a circle or greeting a user. Instead of writing the same code over and over, you can encapsulate it within a function. Functions act as mini-programs within your main program, allowing you to:
10 |
11 | - Modularize Code: Break down your program into smaller, independent units, making it easier to understand and maintain.
12 | - Reuse Code: Avoid repetition by calling the same function multiple times with different inputs.
13 | - Improve Readability: Make your code more organized and easier to follow.
14 |
15 | ### Basic Function Syntax: A Simple Template
16 |
17 | A function in Go is defined using the `func` keyword followed by the function name, its parameters (inputs), and its return values (outputs). Here's a basic template:
18 |
19 | ```go
20 | func functionName(parameter1 type, parameter2 type) returnType {
21 | // Code to be executed within the function
22 | return value // Optional return value
23 | }
24 | ```
25 |
26 | ### Examples: Bringing Functions to Life
27 |
28 | Let's illustrate with a few examples:
29 |
30 | Greeting Function:
31 |
32 | ```go
33 | func greet(name string) {
34 | fmt.Println("Hello,", name)
35 | }
36 |
37 | func main() {
38 | greet("Alice") // Output: "Hello, Alice"
39 | greet("Bob") // Output: "Hello, Bob"
40 | }
41 | ```
42 |
43 | Area Calculation Function:
44 |
45 | ```go
46 | func circleArea(radius float64) float64 {
47 | return 3.14159 * radius * radius
48 | }
49 |
50 | func main() {
51 | area := circleArea(5.0) // Calculate the area of a circle with radius 5.0
52 | fmt.Println("Area:", area) // Output: "Area: 78.53975"
53 | }
54 | ```
55 |
56 | ### Returning Values: Giving Functions Outputs
57 |
58 | Functions can return values to the caller. This allows you to use the results of a function in other parts of your program:
59 |
60 | ```go
61 | func sum(a int, b int) int { // Function that adds two integers
62 | return a + b
63 | }
64 |
65 | func main() {
66 | result := sum(3, 5) // Call the "sum" function and store the result in "result"
67 | fmt.Println("Sum:", result) // Output: "Sum: 8"
68 | }
69 | ```
70 |
71 | ### Early Returns: Exiting Functions Efficiently
72 |
73 | Sometimes you might need to exit a function before reaching the end. This is where early returns come in handy:
74 |
75 | ```go
76 | func divide(a int, b int) int {
77 | if b == 0 {
78 | return 0 // Handle division by zero
79 | }
80 | return a / b
81 | }
82 | ```
83 |
84 | ### Function Declaration Syntax: A Deeper Dive
85 |
86 | Go allows you to declare functions in a few different ways:
87 |
88 | - Standard Declaration: The most common way, as shown in the examples above.
89 | - Named Return Values: You can explicitly name the return values of a function. This can be beneficial for readability and organization:
90 |
91 | ```go
92 | func subtract(a int, b int) (result int) { // "result" is the named return value
93 | result = a - b
94 | return // Return the value of "result" implicitly
95 | }
96 | ```
97 |
98 | ### Benefits of Named Returns: Cleaner and More Readable Code
99 |
100 | Named return values offer a few advantages:
101 |
102 | - Improved Readability: They make your code easier to understand, especially for functions with multiple return values.
103 | - Less Repetition: You don't need to explicitly mention the return values in the `return` statement.
104 |
105 | You've now learned the fundamentals of functions in Go! In future sections, you'll explore more advanced topics like recursion, closures, and how to effectively work with functions in Go's concurrent environment. Armed with this knowledge, you're ready to write clean, organized, and efficient Go code. Happy coding!
106 |
--------------------------------------------------------------------------------
/src/data/interfaces/clean-interfaces.md:
--------------------------------------------------------------------------------
1 | # Clean Interfaces
2 |
3 | Writing clean interfaces is *hard*. Frankly, anytime you’re dealing with abstractions in code, the simple can become complex very quickly if you’re not careful. Let’s go over some rules of thumb for keeping interfaces clean.
4 |
5 | ## 1. Keep Interfaces Small
6 |
7 | If there is only one piece of advice that you take away from this article, make it this: keep interfaces small! Interfaces are meant to define the minimal behavior necessary to accurately represent an idea or concept. Here is an example from the standard HTTP package of a larger interface that’s a good example of defining minimal behavior:
8 |
9 | ```go
10 | type File interface {
11 | io.Closer
12 | io.Reader
13 | io.Seeker
14 | Readdir(count int) ([]os.FileInfo, error)
15 | Stat() (os.FileInfo, error)
16 | }
17 | ```
18 |
19 | Any type that satisfies the interface’s behaviors can be considered by the HTTP package as a *File*. This is convenient because the HTTP package doesn’t need to know if it’s dealing with a file on disk, a network buffer, or a simple `[]byte`.
20 |
21 | ## 2. Interfaces Should Have No Knowledge of Satisfying Types
22 |
23 | An interface should define what is necessary for other types to classify as a member of that interface. They shouldn’t be aware of any types that happen to satisfy the interface at design time. For example, let’s assume we are building an interface to describe the components necessary to define a car.
24 |
25 | ```go
26 | type car interface {
27 | Color() string
28 | Speed() int
29 | IsFiretruck() bool
30 | }
31 | ```
32 |
33 | `Color()` and `Speed()` make perfect sense, they are methods confined to the scope of a car. `IsFiretruck()` is an anti-pattern. We are forcing all cars to declare whether or not they are firetrucks. In order for this pattern to make any amount of sense, we would need a whole list of possible subtypes. `IsPickup()`, `IsSedan()`, `IsTank()`… where does it end?? Instead, the developer should have relied on the native functionality of type assertion to derive the underlying type when given an instance of the car interface. Or, if a sub-interface is needed, it can be defined as:
34 |
35 | ```go
36 | type firetruck interface {
37 | car
38 | HoseLength() int
39 | }
40 | ```
41 |
42 | Which inherits the required methods from `car` and adds one additional required method to make the `car` a `firetruck`.
43 |
44 | ## 3. Interfaces Are Not Classes
45 |
46 | - Interfaces are not classes, they are slimmer.
47 | - Interfaces don’t have constructors or deconstructors that require that data is created or destroyed.
48 | - Interfaces aren’t hierarchical by nature, though there is syntactic sugar to create interfaces that happen to be supersets of other interfaces.
49 | - Interfaces define function signatures, but not underlying behavior. Making an interface often won’t DRY up your code in regards to struct methods. For example, if five types satisfy the `fmt.Stringer` interface, they all need their own version of the `String()` function.
50 |
51 | ## Further reading
52 |
53 | [Best Practices for Interfaces in Go](https://victorpierre.dev/blog/five-go-interfaces-best-practices/)
54 |
--------------------------------------------------------------------------------
/src/data/interfaces/interfaces-in-go.md:
--------------------------------------------------------------------------------
1 | # Interfaces in Go
2 |
3 | Interfaces are collections of method signatures. A type "implements" an interface if it has all of the methods of the given interface defined on it. In the following example, a "shape" must be able to return its area and perimeter. Both `rect` and `circle` fulfill the interface.
4 |
5 | ```go
6 | type shape interface {
7 | area() float64
8 | perimeter() float64
9 | }
10 |
11 | type rect struct {
12 | width, height float64
13 | }
14 | func (r rect) area() float64 {
15 | return r.width * r.height
16 | }
17 | func (r rect) perimeter() float64 {
18 | return 2*r.width + 2*r.height
19 | }
20 |
21 | type circle struct {
22 | radius float64
23 | }
24 | func (c circle) area() float64 {
25 | return math.Pi * c.radius * c.radius
26 | }
27 | func (c circle) perimeter() float64 {
28 | return 2 * math.Pi * c.radius
29 | }
30 | ```
31 |
32 | When a type implements an interface, it can then be used as the interface type.
33 |
34 | ## Interface Implementation
35 |
36 | Interfaces are implemented *implicitly*. A type never declares that it implements a given interface. Unlike in many other languages, there is no explicit declaration of intent, there is no "implements" keyword. If an interface exists and a type has the proper methods defined, then the type automatically fulfills that interface. Implicit interfaces *decouple* the definition of an interface from its implementation. You may add methods to a type and in the process be unknowingly implementing various interfaces, and *that's okay*.
37 |
38 | ### Note
39 |
40 | Remember, interfaces are collections of method signatures. A type "implements" an interface if it has all of the methods of the given interface defined on it.
41 |
42 | ```go
43 | type shape interface {
44 | area() float64
45 | }
46 | ```
47 |
48 | If a type in your code implements an `area` method, with the same signature (e.g. accepts nothing and returns a `float64`), then that object is said to *implement* the `shape` interface.
49 |
50 | ```go
51 | type circle struct{
52 | radius int
53 | }
54 |
55 | func (c *circle) area() float64 {
56 | return 3.14 * c.radius * c.radius
57 | }
58 | ```
59 |
60 | This is *different from most other languages*, where you have to *explicitly* assign an interface type to an object, like with Java:
61 |
62 | ```java
63 | class Circle implements Shape
64 | ```
--------------------------------------------------------------------------------
/src/data/interfaces/multiple-interfaces.md:
--------------------------------------------------------------------------------
1 | # Multiple Interfaces
2 |
3 | A type can implement any number of interfaces in Go. For example, the empty interface, `interface{}`, is *always* implemented by every type because it has no requirements.
4 |
5 | ## Name Your Interface Arguments
6 |
7 | Consider the following interface:
8 |
9 | ```go
10 | type Copier interface {
11 | Copy(string, string) int
12 | }
13 | ```
14 |
15 | Based on the code alone, can you deduce what *kinds* of strings you should pass into the `Copy` function? We know the function signature expects 2 string types, but what are they? Filenames? URLs? Raw string data? For that matter, what the heck is that `int` that's being returned? Let's add some named arguments and return data to make it more clear.
16 |
17 | ```go
18 | type Copier interface {
19 | Copy(sourceFile string, destinationFile string) (bytesCopied int)
20 | }
21 | ```
22 |
23 | Much better. We can see what the expectations are now. The first argument is the `sourceFile`, the second argument is the `destinationFile`, and `bytesCopied`, an integer, is returned.
24 |
--------------------------------------------------------------------------------
/src/data/interfaces/type-assertion.md:
--------------------------------------------------------------------------------
1 | # Type Assertions in Go
2 |
3 | When working with interfaces in Go, every once-in-awhile you'll need access to the underlying type of an interface value. You can cast an interface to its underlying type using a *type assertion*.
4 |
5 | ```go
6 | type shape interface {
7 | area() float64
8 | }
9 |
10 | type circle struct {
11 | radius float64
12 | }
13 |
14 | // "c" is a new circle cast from "s" which is an instance of a shape.
15 | // "ok" is a bool that is true if s was a circle or false if s isn't a circle
16 | c, ok := s.(circle)
17 | ```
18 |
19 | ## Type Switches
20 |
21 | A *type switch* makes it easy to do several type assertions in a series. A type switch is similar to a regular switch statement, but the cases specify *types* instead of *values*.
22 |
23 | ```go
24 | func printNumericValue(num interface{}) {
25 | switch v := num.(type) {
26 | case int:
27 | fmt.Printf("%T\n", v)
28 | case string:
29 | fmt.Printf("%T\n", v)
30 | default:
31 | fmt.Printf("%T\n", v)
32 | }
33 | }
34 |
35 | func main() {
36 | printNumericValue(1)
37 | // prints "int"
38 |
39 | printNumericValue("1")
40 | // prints "string"
41 |
42 | printNumericValue(struct{}{})
43 | // prints "struct {}"
44 | }
45 | ```
46 |
47 | `fmt.Printf("%T\n", v)` prints the *type* of a variable.
48 |
--------------------------------------------------------------------------------
/src/data/interfaces/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Interfaces in Go
4 |
5 | Interfaces in Go are a powerful mechanism for achieving abstraction and polymorphism. They define a contract, outlining the methods that a type must implement to satisfy the interface. This allows you to write code that works with different types in a flexible and adaptable way.
6 |
7 | ### Interfaces: Defining a Contract
8 |
9 | Imagine you have different types of animals: cats, dogs, birds, etc. They might have different behaviors, but they all share certain common actions like making sounds or moving. An interface defines these shared actions:
10 |
11 | ```go
12 | type Animal interface {
13 | Speak() string
14 | Move() string
15 | }
16 | ```
17 |
18 | This `Animal` interface defines two methods: `Speak()` and `Move()`. Any type that implements these methods will satisfy the `Animal` interface.
19 |
20 | ### Implementing Interfaces: Types Conforming to the Contract
21 |
22 | Let's define a `Cat` and a `Dog` type that implement the `Animal` interface:
23 |
24 | ```go
25 | type Cat struct{}
26 |
27 | func (c Cat) Speak() string {
28 | return "Meow"
29 | }
30 |
31 | func (c Cat) Move() string {
32 | return "Walking gracefully"
33 | }
34 |
35 | type Dog struct{}
36 |
37 | func (d Dog) Speak() string {
38 | return "Woof"
39 | }
40 |
41 | func (d Dog) Move() string {
42 | return "Running excitedly"
43 | }
44 | ```
45 |
46 | Now, both `Cat` and `Dog` satisfy the `Animal` interface because they implement the required methods.
47 |
48 | ### Using Interfaces: Working with Different Types
49 |
50 | The beauty of interfaces is that you can write code that works with any type that implements the interface:
51 |
52 | ```go
53 | func makeAnimalSound(animal Animal) {
54 | fmt.Println(animal.Speak())
55 | }
56 |
57 | func main() {
58 | cat := Cat{}
59 | dog := Dog{}
60 |
61 | makeAnimalSound(cat) // Output: "Meow"
62 | makeAnimalSound(dog) // Output: "Woof"
63 | }
64 | ```
65 |
66 | The `makeAnimalSound` function accepts an `Animal` interface as an argument. This means it can work with any type that implements the `Animal` interface, such as `Cat` or `Dog`.
67 |
68 | ### Multiple Interfaces: Embracing Flexibility
69 |
70 | A type can implement multiple interfaces:
71 |
72 | ```go
73 | type Bird struct{}
74 |
75 | func (b Bird) Speak() string {
76 | return "Tweet"
77 | }
78 |
79 | func (b Bird) Fly() string {
80 | return "Soaring through the sky"
81 | }
82 |
83 | func main() {
84 | bird := Bird{}
85 |
86 | makeAnimalSound(bird) // Output: "Tweet"
87 | //makeBirdFly(bird) // You can use bird with a function accepting a Bird interface
88 | }
89 | ```
90 |
91 | ### Clean Interfaces: Less is More
92 |
93 | Aim for clean and concise interfaces that focus on a specific set of related actions. This makes your interfaces more reusable and adaptable.
94 |
95 | ### Type Assertion: Verifying Interface Implementations
96 |
97 | Sometimes you might need to know the specific type that implements an interface. Type assertion allows you to check and cast an interface value to its underlying type:
98 |
99 | ```go
100 | func main() {
101 | animal := Cat{}
102 |
103 | if cat, ok := animal.(Cat); ok {
104 | fmt.Println("It's a Cat:", cat.Speak())
105 | } else {
106 | fmt.Println("It's not a Cat")
107 | }
108 | }
109 | ```
110 |
111 | You've learned the fundamentals of interfaces in Go! If you want to dig more, you can explore advanced topics like embedding interfaces, empty interfaces, and how interfaces are used in Go's concurrency model. With this understanding, you'll be well-equipped to leverage the power of abstraction and polymorphism in your Go programs.
112 |
--------------------------------------------------------------------------------
/src/data/intro/basic-syntax.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanuelCh/gopher-notes/2ac2c6a501f409a84481efb0a5a526ba45577dd2/src/data/intro/basic-syntax.md
--------------------------------------------------------------------------------
/src/data/intro/compiled-vs-interpreted.md:
--------------------------------------------------------------------------------
1 | # Compiled vs Interpreted
2 |
3 | Compiled programs can be run without access to the original source code, and without access to a compiler. This is different than interpreted languages like Python and JavaScript. With Python and JavaScript the code is interpreted at [runtime](https://en.wikipedia.org/wiki/Runtime_(program_lifecycle_phase)) by a separate program known as the "interpreter". Distributing code for users to run can be a pain because they need to have an interpreter installed, and they need access to the original source code.
4 |
5 | 
6 |
7 | One of the most convenient things about using a compiled language like Go for backend services is that when we deploy our server we don't need to include any runtime language dependencies like Node or a Python interpreter. We just add the pre-compiled binary to the server and start it up!
--------------------------------------------------------------------------------
/src/data/intro/compiling.md:
--------------------------------------------------------------------------------
1 | # Compiling
2 |
3 | Go is a compiled programming language. To define the term "compiled", computers need machine code, they don't understand English or even uncompiled computer programs. For example, the code
4 |
5 | ```go
6 | package main
7 |
8 | import "fmt"
9 |
10 | func main(){
11 | fmt.Println("hello world")
12 | }
13 | ```
14 |
15 | means *nothing* to a computer. We need to convert our high-level (Go) code into machine language, which is really just a set of instructions that some specific hardware can understand. In your case, your CPU. The Go compiler's job is to take Go code and produce machine code. On Windows, that would be a `.exe` file. On Mac or Linux, it would be any executable file.
16 |
17 | ## Computers Need Machine Code
18 |
19 | A computer's [CPU](https://en.wikipedia.org/wiki/Central_processing_unit) only understands its own *instruction set*, which we call "machine code". Instructions are basic math operations like addition, subtraction, multiplication, and the ability to save data temporarily. For example, an [ARM processor](https://en.wikipedia.org/wiki/ARM_architecture) uses the *ADD* instruction when supplied with the number `0100` in binary. Go, C, and Rust are all languages where the code is first converted to machine code by the compiler before it's executed.
20 |
21 | 
22 |
23 |
24 | ## How Fast is Go ?
25 |
26 | Generally speaking, compiled languages run much faster than interpreted languages or VM-powered languages, and Go is no exception. Go is one of the fastest programming languages, beating JavaScript, Python, and Ruby handily in most benchmarks. However, Go code doesn't *run* quite as fast as its compiled Rust and C counterparts. That said, it *compiles* much faster than they do, which makes the developer experience super productive. Unfortunately, there are no swordfights on Go teams...
27 |
28 | 
29 |
--------------------------------------------------------------------------------
/src/data/intro/go-introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction to Go (Golang)
2 |
3 | Go is a statically typed, compiled programming language designed by Google. It's all about simplicity and efficiency, making it super easy to write clean and maintainable code. Think of it as the cool kid on the block that combines the best features of C, C++, and Python, but with its own unique flair!
4 |
5 | ## Key Features of Go
6 |
7 | - **Simplicity:** Go's syntax is clean and easy to read. No more wrestling with complicated language features!
8 | - **Concurrency:** Go makes it a breeze to handle multiple tasks at once with goroutines and channels. It's like having a squad of mini-helpers working in the background!
9 | - **Fast Compilation:** Go compiles super quickly, so you can spend less time waiting and more time coding. Instant gratification, anyone?
10 | - **Garbage Collection:** No need to manually manage memory. Go's garbage collector takes care of that for you, freeing you up to focus on what really matters!
11 | - **Strong Standard Library:** Go comes with a robust standard library that provides tons of built-in functions for common tasks. It's like having a Swiss Army knife in your coding toolkit!
12 | - **Cross-Platform:** Write your code once, and it can run on multiple platforms without a hitch. Go is all about that portability life!
13 |
14 | ## Historical Background
15 |
16 | Go was created in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson at Google. They wanted to address shortcomings in other languages while keeping things simple and efficient. After a couple of years of development, Go was officially released to the public in 2012. Since then, it's gained a massive following and is now used by companies like Google, Uber, and Dropbox. Talk about a glow-up!
17 |
18 | ## Use Cases
19 |
20 | So, what can you actually do with Go? Here are some popular use cases:
21 |
22 | - **Web Development:** Build high-performance web servers and APIs with frameworks like Gin and Echo. Perfect for those who want to create scalable web applications!
23 | - **Cloud Services:** Go is a go-to language for cloud-based applications and microservices. It's lightweight and efficient, making it ideal for distributed systems.
24 | - **DevOps Tools:** Many DevOps tools, like Docker and Kubernetes, are written in Go. If you're into automation and containerization, Go is your jam!
25 | - **Networking:** With its powerful concurrency model, Go excels at building networking tools and applications. Think chat servers, proxies, and more!
26 |
27 | ## A Glance at a Go Program
28 |
29 | ```go
30 | package main
31 |
32 | import "fmt"
33 |
34 | func main() {
35 | fmt.Println("hello world")
36 | }
37 |
38 | ```
39 |
40 | Here are a few tidbits about the code:
41 |
42 | 1. `package main` lets the Go compiler know that we want this code to compile and run as a standalone program, as opposed to being a library that's imported by other programs.
43 | 2. `import fmt` imports the `fmt` (formatting) package. The formatting package exists in Go's standard library and let's us do things like print text to the console.
44 | 3. `func main()` defines the `main` function. `main` is the name of the function that acts as the entry point for a Go program.
--------------------------------------------------------------------------------
/src/data/intro/go-is-strongly-typed.md:
--------------------------------------------------------------------------------
1 | # Go is Strongly Typed
2 |
3 | Go enforces strong and static typing, meaning variables can only have a single type. A `string` variable like "hello world" can not be changed to an `int`, such as the number `3`. One of the biggest benefits of strong typing is that errors can be caught at "compile time". In other words, bugs are more easily caught ahead of time because they are detected when the code is compiled before it even runs. Contrast this with most interpreted languages, where the variable types are dynamic. Dynamic typing can lead to subtle bugs that are hard to detect. With interpreted languages, the code *must* be run (sometimes in production if you are unlucky 😨) to catch syntax and type errors.
4 |
5 | ## Concatenating strings
6 |
7 | Two strings can be [concatenated](https://en.wikipedia.org/wiki/Concatenation) with the `+` operator. Because Go is strongly typed, it won't allow you to concatenate a string variable with a numeric variable.
--------------------------------------------------------------------------------
/src/data/intro/go-memory-usage.md:
--------------------------------------------------------------------------------
1 | # Go Programs are Easy on Memory
2 |
3 | Go programs are fairly lightweight. Each program includes a small amount of "extra" code that's included in the executable binary. This extra code is called the [Go Runtime](https://go.dev/doc/faq#runtime). One of the purposes of the Go runtime is to cleanup unused memory at runtime. In other words, the Go compiler includes a small amount of extra logic in every Go program to make it easier for developers to write code that's memory efficient.
4 |
5 | ## Comparison
6 |
7 | As a general rule Java programs use *more* memory than comparable Go programs because Go doesn't use an entire virtual machine to run its programs, just a small runtime. The Go runtime is small enough that it is included directly in each Go program's compiled machine code. As another general rule Rust and C++ programs use slightly *less* memory than Go programs because more control is given to the developer to optimize memory usage of the program. The Go runtime just handles it for us automatically.
8 |
9 | ## Idle Memory Usage
10 |
11 | 
12 |
13 | In the chart above, [Dexter Darwich compares the memory usage](https://medium.com/@dexterdarwich/comparison-between-java-go-and-rust-fdb21bd5fb7c) of three *very* simple programs written in Java, Go, and Rust. As you can see, Go and Rust use *very* little memory when compared to Java.
14 |
--------------------------------------------------------------------------------
/src/data/intro/requirements.md:
--------------------------------------------------------------------------------
1 | # Requirements
2 |
3 | Take a look at the ideas below to get the most out of this guide. If you're familiar with Go, ignore this section and read the topic of your choice.
4 |
5 | ## Don't skip the basics
6 |
7 | If it's your first time learning Go, don't skip the basics of the language like variables and types, functions, structs and so. You can then jump around the topics and refer to the guide as per your interest.
8 |
9 | ## Do the exercises
10 |
11 | This one is an important one. Your learning is tested by doing the exercises provided. Navigate to the [exercises section](/exercises) and try to solve as many exercises as possible. Plus, consider doing challenges on [Codewars](https://www.codewars.com) or other similar platforms.
12 |
13 | ## Read the **"wrapping up"** sections
14 |
15 | Each topic has a **"wrapping up"** section where you can get a glimpse of what was covered and analyze the pieces quickly. Even if you skipped a specific topic, you can read the wrapping up section to get familiar with the topic.
16 |
17 | ## Make sure to understand the code snippet
18 |
19 | While following the guide, you might come across lots of code snippets. Make sure to analyze and understand the code snippets. You can also run them on your local machine. Each snippet has a comment so it's easier for you to understand what the code is doing. If still you don't get the code snippet, google about it or ask AI (perks of living in AI era lol).
20 |
21 | ## Track your learning
22 |
23 | To stay organized and use your time efficiently, track your learning. Also consider [golang roadmap](https://roadmap.sh/golang). It provides you with a decent roadmap and an ability to track your progress. Upon completion of this guide, you can move on to more specialized area of Go like a career as a Backend developer or streamlining your devops work.
24 |
25 | That's it mates. Feel free to use the guide as per your need. You can contact me at [amanuelchaka2@gmail.com](mailto:amanuelchaka2@gmail.com) or my [Telegram account](https://t.me/sozoFe). You can also drop an issue on the guide's [GitHub repo](https://github.com/AmanuelCh/gopher-notes).
26 |
--------------------------------------------------------------------------------
/src/data/intro/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | So you're ready to dive into the world of Go, a language known for its speed, simplicity, and efficiency. A quick recap on what was covered:
4 |
5 | ## What is Go?
6 |
7 | Go, often affectionately referred to as "Golang," was born from the minds of Google engineers. It was designed to tackle the challenges of building large, complex systems. Think of it as a language that makes your code easy to read, write, and maintain while still being lightning fast.
8 |
9 | ### Go's Basic Syntax: Simplicity is Key
10 |
11 | Go is known for its elegant and straightforward syntax. You'll find it quite intuitive if you've worked with languages like C or Java. Here are a few key elements:
12 |
13 | Hello, World!: The classic first program:
14 | ```go
15 | package main
16 |
17 | import "fmt"
18 |
19 | func main() {
20 | fmt.Println("Hello, World!")
21 | }
22 | ```
23 |
24 | ### The Power of Compiled Languages
25 |
26 | Go is a compiled language, which means your code is translated directly into machine code (the language your computer understands) before it can be run. This translation process is done by a compiler.
27 |
28 | Benefits of Compiling:
29 |
30 | - Fast Execution: Compiled code runs much faster than interpreted code because it doesn't need to be translated at runtime.
31 | - Efficiency: The compiler optimizes your code for performance
32 |
33 | ### Memory Management: Go Takes Care of the Heavy Lifting
34 |
35 | Go is a garbage-collected language. This means you don't need to manually manage memory (like you might in C or C++). Go's garbage collector automatically cleans up unused memory, saving you from potential memory leaks and making your life easier.
36 |
37 | ### Go's Concurrency: Making Things Happen Simultaneously
38 |
39 | Go is designed for concurrency, meaning you can run multiple tasks at the same time. This is achieved through goroutines, which are lightweight threads that allow you to write highly efficient concurrent programs.
40 |
41 | Ready for More?
42 |
43 | You've now laid a solid foundation in Go! We've explored the language's key features, its simplicity, and its power. In the next sections, you'll dive into more advanced concepts like functions, data structures, and concurrency.
44 |
45 | Go is a language that empowers you to build performant and reliable applications, and you're now equipped with the essential knowledge to get started. Happy coding!
46 |
--------------------------------------------------------------------------------
/src/data/loops/continue-and-break.md:
--------------------------------------------------------------------------------
1 | # The Continue and Break Keyword
2 |
3 | ## Continue
4 |
5 | The `continue` keyword stops the current iteration of a loop and continues to the next iteration. `continue` is a powerful way to use the "guard clause" pattern within loops.
6 |
7 | ```go
8 | for i := 0; i < 10; i++ {
9 | if i % 2 == 0 {
10 | continue
11 | }
12 | fmt.Println(i)
13 | }
14 | // 1
15 | // 3
16 | // 5
17 | // 7
18 | // 9
19 | ```
20 |
21 | ## Break
22 |
23 | The `break` keyword stops the current iteration of a loop and exits the loop.
24 |
25 | ```go
26 | for i := 0; i < 10; i++ {
27 | if i == 5 {
28 | break
29 | }
30 | fmt.Println(i)
31 | }
32 | // 0
33 | // 1
34 | // 2
35 | // 3
36 | // 4
37 | ```
38 |
--------------------------------------------------------------------------------
/src/data/loops/intro.md:
--------------------------------------------------------------------------------
1 | # Loops in Go
2 |
3 | The basic loop in Go is written in standard C-like syntax:
4 |
5 | ```go
6 | for INITIAL; CONDITION; AFTER{
7 | // do something
8 | }
9 | ```
10 |
11 | - `INITIAL` is run once at the beginning of the loop and can create variables within the scope of the loop.
12 | - `CONDITION` is checked before each iteration. If the condition doesn't pass then the loop breaks.
13 | - `AFTER` is run after each iteration.
14 |
15 | For example:
16 |
17 | ```go
18 | for i := 0; i < 10; i++ {
19 | fmt.Println(i)
20 | // Prints 0 through 9
21 | }
22 | ```
23 |
24 | What makes Go different from other languages is that "`for`" is the only looping keyword in the language. Go accomplishes this by using the for keyword in four formats:
25 |
26 | - A complete, C-style for
27 | - A condition-only for
28 | - An infinite for
29 | - for-range
30 |
--------------------------------------------------------------------------------
/src/data/loops/omitting-conditions.md:
--------------------------------------------------------------------------------
1 | # Omitting Conditions
2 |
3 | Loops in Go can omit sections of a for loop. For example, the `CONDITION` (middle part) can be omitted which causes the loop to run forever.
4 |
5 | ```go
6 | for INITIAL; ; AFTER {
7 | // do something forever
8 | }
9 | ```
10 |
11 | you’ll leave off the initialization if it is based on a value calculated before the loop:
12 |
13 | ```go
14 | i := 0
15 | for ; i < 10; i++ {
16 | fmt.Println(i)
17 | }
18 | ```
19 |
20 | or you’ll leave off the increment because you have a more complicated increment rule
21 | inside the loop:
22 |
23 | ```go
24 | for i := 0; i < 10; {
25 | fmt.Println(i)
26 | if i % 2 == 0 {
27 | i++
28 | } else {
29 | i+=2
30 | }
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/src/data/loops/operators.md:
--------------------------------------------------------------------------------
1 | # Operators
2 |
3 | Go supports the standard [modulo operator](https://en.wikipedia.org/wiki/Modulo_operation):
4 |
5 | ```go
6 | 7 % 3 // 1
7 | ```
8 |
9 | Logical [AND operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND):
10 |
11 | ```go
12 | true && false // false
13 | true && true // true
14 | ```
15 |
16 | Logical [OR operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR):
17 |
18 | ```go
19 | true || false // true
20 | false || false // false
21 | ```
22 |
--------------------------------------------------------------------------------
/src/data/loops/while-loop.md:
--------------------------------------------------------------------------------
1 | # There is No while Loop in Go
2 |
3 | Most programming languages have a concept of a `while` loop. Because Go allows for the omission of sections of a `for` loop, a `while` loop is just a `for` loop that only has a CONDITION.
4 |
5 | ```go
6 | for CONDITION {
7 | // do some stuff while CONDITION is true
8 | }
9 | ```
10 |
11 | For example:
12 |
13 | ```go
14 | plantHeight := 1
15 | for plantHeight < 5 {
16 | fmt.Println("still growing! current height:", plantHeight)
17 | plantHeight++
18 | }
19 | fmt.Println("plant has grown to ", plantHeight, "inches")
20 | ```
21 |
22 | Which prints:
23 |
24 | ```go
25 | still growing! current height: 1
26 | still growing! current height: 2
27 | still growing! current height: 3
28 | still growing! current height: 4
29 | plant has grown to 5 inches
30 | ```
31 |
--------------------------------------------------------------------------------
/src/data/loops/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Looping Through Your Code
4 |
5 | Loops are essential tools in programming for repeating actions or iterating over data. Go provides two primary loop structures: `for` and `for...range`.
6 |
7 | ### Introduction to Loops: Repeating Actions
8 |
9 | Loops allow you to execute a block of code multiple times, either for a predetermined number of iterations or until a specific condition is met. This avoids repetitive code and makes your programs more dynamic.
10 |
11 | ### The `for` Loop: Go's Workhorse
12 |
13 | The `for` loop is the most versatile loop in Go. It has a simple structure:
14 |
15 | ```go
16 | for initialization; condition; post-iteration {
17 | // Code to be executed repeatedly
18 | }
19 | ```
20 |
21 | - Initialization: Executed once at the beginning of the loop.
22 | - Condition: Evaluated before each iteration. The loop continues as long as the condition is true.
23 | - Post-iteration: Executed after each iteration.
24 |
25 | ### Examples: Bringing Loops to Life
26 |
27 | Counting to 10:
28 |
29 | ```go
30 | for i := 0; i < 10; i++ {
31 | fmt.Println(i)
32 | }
33 | ```
34 |
35 | Iterating over a slice:
36 |
37 | ```go
38 | numbers := []int{1, 2, 3, 4, 5}
39 | for i := 0; i < len(numbers); i++ {
40 | fmt.Println(numbers[i])
41 | }
42 | ```
43 |
44 | ### Loop Operators: Controlling Iteration
45 |
46 | - `++` (Increment): Increases the value of a variable by 1.
47 | - `--` (Decrement): Decreases the value of a variable by 1.
48 | - `+=` (Add and Assign): Adds a value to a variable and assigns the result to the variable.
49 | - `-=` (Subtract and Assign): Subtracts a value from a variable and assigns the result to the variable.
50 |
51 | ### Omitting Loop Conditions: Infinite Loops
52 |
53 | You can omit the condition in a `for` loop to create an infinite loop:
54 |
55 | ```go
56 | for {
57 | // Code that runs indefinitely
58 | }
59 | ```
60 |
61 | ### The `continue` Keyword: Skipping to the Next Iteration
62 |
63 | The `continue` keyword jumps to the beginning of the next iteration of the loop, skipping any remaining code in the current iteration:
64 |
65 | ```go
66 | for i := 0; i < 10; i++ {
67 | if i%2 == 0 {
68 | continue // Skip even numbers
69 | }
70 | fmt.Println(i)
71 | }
72 | ```
73 |
74 | ### The `break` Keyword: Exiting the Loop
75 |
76 | The `break` keyword exits the loop completely, ending the loop's execution:
77 |
78 | ```go
79 | for i := 0; i < 10; i++ {
80 | if i == 5 {
81 | break // Exit the loop when i is 5
82 | }
83 | fmt.Println(i)
84 | }
85 | ```
86 |
--------------------------------------------------------------------------------
/src/data/maps/key-types.md:
--------------------------------------------------------------------------------
1 | # Key Types
2 |
3 | Any type can be used as the *value* in a map, but *keys* are more restrictive. Read the following section of the official [Go blog](https://go.dev/blog/maps):
4 |
5 | As mentioned earlier, **map keys may be of any type that is comparable**. The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types. Notably absent from the list are slices, maps, and functions; these types cannot be compared using `==`, and may not be used as map keys.
6 |
7 | It's obvious that strings, ints, and other basic types should be available as map keys, but perhaps unexpected are struct keys. Struct can be used to key data by multiple dimensions. For example, this map of maps could be used to tally web page hits by country:
8 |
9 | ```go
10 | hits := make(map[string]map[string]int)
11 | ```
12 |
13 | This is map of string to (map of string to int). Each key of the outer map is the path to a web page with its own inner map. Each inner map key is a two-letter country code. This expression retrieves the number of times an Australian has loaded the documentation page:
14 |
15 | ```go
16 | n := hits["/doc/"]["au"]
17 | ```
18 |
19 | Unfortunately, this approach becomes unwieldy when adding data, as for any given outer key you must check if the inner map exists, and create it if needed:
20 |
21 | ```go
22 | func add(m map[string]map[string]int, path, country string) {
23 | mm, ok := m[path]
24 | if !ok {
25 | mm = make(map[string]int)
26 | m[path] = mm
27 | }
28 | mm[country]++
29 | }
30 | add(hits, "/doc/", "au")
31 | ```
32 |
33 | On the other hand, a design that uses a single map with a struct key does away with all that complexity:
34 |
35 | ```go
36 | type Key struct {
37 | Path, Country string
38 | }
39 | hits := make(map[Key]int)
40 | ```
41 |
42 | When a Vietnamese person visits the home page, incrementing (and possibly creating) the appropriate counter is a one-liner:
43 |
44 | ```go
45 | hits[Key{"/", "vn"}]++
46 | ```
47 |
48 | And it’s similarly straightforward to see how many Swiss people have read the spec:
49 |
50 | ```go
51 | n := hits[Key{"/ref/spec", "ch"}]
52 | ```
53 |
--------------------------------------------------------------------------------
/src/data/maps/maps-in-go.md:
--------------------------------------------------------------------------------
1 | # Maps
2 |
3 | Maps are similar to JavaScript objects, Python dictionaries, and Ruby hashes. Maps are a data structure that provides key -> value mapping. The zero value of a map is `nil`. We can create a map by using a literal or by using the `make()` function:
4 |
5 | ```go
6 | ages := make(map[string]int)
7 | ages["John"] = 37
8 | ages["Mary"] = 24
9 | ages["Mary"] = 21 // overwrites 24
10 | ```
11 |
12 | ```go
13 | ages := map[string]int{
14 | "John": 37,
15 | "Mary": 21,
16 | }
17 | ```
18 |
19 | The `len()` function works on a map, it returns the total number of key/value pairs.
20 |
21 | ```go
22 | ages := map[string]int{
23 | "John": 37,
24 | "Mary": 21,
25 | }
26 | fmt.Println(len(ages)) // 2
27 | ```
28 |
--------------------------------------------------------------------------------
/src/data/maps/maps-review.md:
--------------------------------------------------------------------------------
1 | # Maps Review
2 |
3 | Like slices, maps hold references to an underlying data structure. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
4 |
5 | ## Map Literals
6 |
7 | Maps can be constructed using the usual composite literal syntax with colon-separated key-value pairs, so it's easy to build them during initialization.
8 |
9 | ```go
10 | var timeZone = map[string]int{
11 | "UTC": 0*60*60,
12 | "EST": -5*60*60,
13 | "CST": -6*60*60,
14 | "MST": -7*60*60,
15 | "PST": -8*60*60,
16 | }
17 | ```
18 |
19 | ## Missing keys
20 |
21 | An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.
22 |
23 | ```go
24 | attended := map[string]bool{
25 | "Ann": true,
26 | "Joe": true,
27 | ...
28 | }
29 |
30 | if attended[person] { // will be false if person is not in the map
31 | fmt.Println(person, "was at the meeting")
32 | }
33 | ```
34 |
35 | Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that 0 because it's not in the map at all? You can discriminate with a form of multiple assignment.
36 |
37 | ```go
38 | var seconds int
39 | var ok bool
40 | seconds, ok = timeZone[tz]
41 | ```
42 |
43 | For obvious reasons, this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:
44 |
45 | ```go
46 | func offset(tz string) int {
47 | if seconds, ok := timeZone[tz]; ok {
48 | return seconds
49 | }
50 | log.Println("unknown time zone:", tz)
51 | return 0
52 | }
53 | ```
54 |
55 | ## Deleting map entries
56 |
57 | To delete a map entry, use the delete built-in function, whose arguments are the map and the key to be deleted. It's safe to do this even if the key is already absent from the map.
58 |
59 | ```go
60 | delete(timeZone, "PDT") // Now on Standard Time
61 | ```
62 |
--------------------------------------------------------------------------------
/src/data/maps/mutating-maps.md:
--------------------------------------------------------------------------------
1 | # Mutations on Maps
2 |
3 | Maps can be mutated.
4 |
5 | ```go
6 | m := map[string]int{
7 | "John": 37,
8 | "Mary": 21,
9 | }
10 | ```
11 |
12 | ## Insert an element
13 |
14 | ```go
15 | m[key] = elem
16 |
17 | // example
18 | m["Amanuel"] = 20
19 |
20 | fmt.Println(m) // map[Amanuel:20 John:37 Marry:21]
21 |
22 | ```
23 |
24 | ## Get an element
25 |
26 | ```go
27 | elem := m[key]
28 |
29 | // example
30 | ageJohn := m["John"]
31 |
32 | fmt.Println(ageJohn) // 37
33 | ```
34 |
35 | ## Delete an element
36 |
37 | ```go
38 | delete(m, key)
39 |
40 | // example
41 | delete(m, "John")
42 |
43 | fmt.Println(m) // map[Amanuel:20 Marry:21]
44 | ```
45 |
46 | ## Check if a key exists
47 |
48 | ```go
49 | elem, ok := m[key]
50 | ```
51 |
52 | If `key` is in `m`, then `ok` is `true`. If not, `ok` is `false`.
53 |
54 | If `key` is not in the map, then `elem` is the zero value for the map's element type.
55 |
--------------------------------------------------------------------------------
/src/data/maps/nested-maps.md:
--------------------------------------------------------------------------------
1 | # Nested Maps
2 |
3 | Maps can contain maps, creating a nested structure. For example:
4 |
5 | ```go
6 | map[string]map[string]int
7 | map[rune]map[string]int
8 | map[int]map[string]map[string]int
9 | ```
10 |
11 | ## Counting Instances
12 |
13 | Remember that you can check if a key is already present in a map by using the second return value from the index operation.
14 |
15 | ```go
16 | names := map[string]int{}
17 |
18 | if _, ok := names["elon"]; !ok {
19 | // if the key doesn't exist yet,
20 | // initialize its value to 0
21 | names["elon"] = 0
22 | }
23 | ```
24 |
--------------------------------------------------------------------------------
/src/data/maps/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Mapping Your Data
4 |
5 | Maps are a powerful data structure in Go for storing key-value pairs, offering a flexible and efficient way to associate data. Think of them as dictionaries, where you can quickly access values based on their unique keys.
6 |
7 | ### Maps in Go: Key-Value Pairs
8 |
9 | Maps are declared using the `map` keyword, followed by the key type and the value type, enclosed in square brackets.
10 |
11 | ```go
12 | ages := map[string]int{
13 | "Alice": 25,
14 | "Bob": 30,
15 | "Charlie": 28,
16 | }
17 | ```
18 |
19 | - Key: A unique identifier for accessing a specific value.
20 | - Value: The data associated with the key.
21 |
22 | ### Accessing Values: Retrieving Data by Key
23 |
24 | You use the key to access the corresponding value:
25 |
26 | ```go
27 | aliceAge := ages["Alice"] // Retrieve Alice's age
28 | fmt.Println(aliceAge) // Output: 25
29 | ```
30 |
31 | ### Mutating Maps: Adding, Updating, and Deleting
32 |
33 | Maps are mutable, allowing you to modify their contents:
34 |
35 | ```go
36 | // Adding a new key-value pair:
37 | ages["David"] = 22
38 |
39 | // Updating a value:
40 | ages["Alice"] = 26
41 |
42 | // Deleting a key-value pair:
43 | delete(ages, "Charlie")
44 | ```
45 |
46 | ### Checking for Keys: Verifying Existence
47 |
48 | You can use the comma-ok idiom to check if a key exists in a map before accessing its value:
49 |
50 | ```go
51 | if age, ok := ages["Bob"]; ok {
52 | fmt.Println("Bob's age is", age)
53 | } else {
54 | fmt.Println("Bob's age is not available")
55 | }
56 | ```
57 |
58 | ### Nested Maps: Structuring Data Hierarchically
59 |
60 | You can create nested maps to organize data in a hierarchical way:
61 |
62 | ```go
63 | users := map[string]map[string]string{
64 | "Alice": {"email": "alice@example.com", "city": "New York"},
65 | "Bob": {"email": "bob@example.com", "city": "London"},
66 | }
67 | ```
68 |
69 | ### Key Types: Supporting a Variety of Keys
70 |
71 | Maps can use various data types for their keys, including strings, integers, structs, and more.
72 |
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/constraints.md:
--------------------------------------------------------------------------------
1 | # Constraints
2 |
3 | Sometimes you need the logic in your generic function to know *something* about the types it operates on. The example we saw in the intro section didn't need to know *anything* about the types in the slice, so we used the built-in `any` constraint:
4 |
5 | ```go
6 | func splitAnySlice[T any](s []T) ([]T, []T) {
7 | mid := len(s)/2
8 | return s[:mid], s[mid:]
9 | }
10 | ```
11 |
12 | Constraints are just interfaces that allow us to write generics that only operate within the constraint of a given interface type. In the example above, the `any` constraint is the same as the empty interface because it means the type in question can be *anything*.
13 |
14 | ## Creating a custom constraint
15 |
16 | Let's take a look at the example of a `concat` function. It takes a slice of values and concatenates the values into a string. This should work with *any type that can represent itself as a string*, even if it's not a string under the hood. For example, a `user` struct can have a `.String()` that returns a string with the user's name and age.
17 |
18 | ```go
19 | type stringer interface {
20 | String() string
21 | }
22 |
23 | func concat[T stringer](vals []T) string {
24 | result := ""
25 | for _, val := range vals {
26 | // this is where the .String() method is used. That's why we need a more specific
27 | // constraint instead of the any constraint
28 | result += val.String()
29 | }
30 | return result
31 | }
32 | ```
33 |
34 | ## Interface type lists
35 |
36 | When generics were released, a new way of writing interfaces was also released at the same time!
37 |
38 | We can now simply list a bunch of types to get a new interface/constraint.
39 |
40 | ```go
41 | // Ordered is a type constraint that matches any ordered type.
42 | // An ordered type is one that supports the <, <=, >, and >= operators.
43 | type Ordered interface {
44 | ~int | ~int8 | ~int16 | ~int32 | ~int64 |
45 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
46 | ~float32 | ~float64 |
47 | ~string
48 | }
49 | ```
50 |
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/generics-in-go.md:
--------------------------------------------------------------------------------
1 | # Generics in Go
2 |
3 | As we've mentioned, Go does *not* support classes. For a long time, that meant that Go code couldn't easily be reused in many circumstances. For example, imagine some code that splits a slice into 2 equal parts. The code that splits the slice doesn't really care about the *values* stored in the slice. Unfortunately in Go we would need to write it multiple times for each type, which is a very un-[DRY](https://blog.boot.dev/clean-code/dry-code/) thing to do.
4 |
5 | ```go
6 | func splitIntSlice(s []int) ([]int, []int) {
7 | mid := len(s)/2
8 | return s[:mid], s[mid:]
9 | }
10 | ```
11 |
12 | ```go
13 | func splitStringSlice(s []string) ([]string, []string) {
14 | mid := len(s)/2
15 | return s[:mid], s[mid:]
16 | }
17 | ```
18 |
19 | In Go 1.20 however, support for [generics](https://blog.boot.dev/golang/how-to-use-golangs-generics/) was released, effectively solving this problem!
20 |
21 | ## Type Parameters
22 |
23 | Put simply, generics allow us to use variables to refer to specific types. This is an amazing feature because it allows us to write abstract functions that drastically reduce code duplication.
24 |
25 | ```go
26 | func splitAnySlice[T any](s []T) ([]T, []T) {
27 | mid := len(s)/2
28 | return s[:mid], s[mid:]
29 | }
30 | ```
31 |
32 | In the example above, `T` is the name of the type parameter for the `splitAnySlice` function, and we've said that it must match the `any` constraint, which means it can be anything. This makes sense because the body of the function *doesn't care* about the types of things stored in the slice.
33 |
34 | ```go
35 | firstInts, secondInts := splitAnySlice([]int{0, 1, 2, 3})
36 | fmt.Println(firstInts, secondInts)
37 | ```
38 |
39 | ## Why Generics?
40 |
41 | ### Generics reduce repetitive code
42 |
43 | You should care about generics because they mean you don’t have to write as much code! It can be frustrating to write the same logic over and over again, just because you have some underlying data types that are slightly different.
44 |
45 | ### Generics are used more often in libraries and packages
46 |
47 | Generics give Go developers an elegant way to write amazing utility packages. While you will see and use generics in application code, I think it will much more common to see generics used in libraries and packages. Libraries and packages contain importable code intended to be used in *many* applications, so it makes sense to write them in a more abstract way. Generics are often the way to do just that!
48 |
49 | ### Why did it take so long to get generics?
50 |
51 | Go places an emphasis on simplicity. In other words, Go has purposefully left out many features to provide its best feature: being simple and easy to work with.
52 |
53 | According to [historical data from Go surveys](https://go.dev/blog/survey2020-results), Go’s lack of generics has always been listed as one of the top three biggest issues with the language. At a certain point, the drawbacks associated with the lack of a feature like generics justify adding complexity to the language.
54 |
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/more-on-mutexes.md:
--------------------------------------------------------------------------------
1 | # More on mutexes
2 |
3 | The principle problem that mutexes help us avoid is the *concurrent read/write problem*. This problem arises when one thread is writing to a variable while another thread is reading from that same variable *at the same time*.
4 |
5 | When this happens, a Go program will panic because the reader could be reading bad data while it's being mutated in place.
6 |
7 | 
8 |
9 | ## Mutex example
10 |
11 | ```go
12 | package main
13 |
14 | import (
15 | "fmt"
16 | )
17 |
18 | func main() {
19 | m := map[int]int{}
20 | go writeLoop(m)
21 | go readLoop(m)
22 |
23 | // stop program from exiting, must be killed
24 | block := make(chan struct{})
25 | <-block
26 | }
27 |
28 | func writeLoop(m map[int]int) {
29 | for {
30 | for i := 0; i < 100; i++ {
31 | m[i] = i
32 | }
33 | }
34 | }
35 |
36 | func readLoop(m map[int]int) {
37 | for {
38 | for k, v := range m {
39 | fmt.Println(k, "-", v)
40 | }
41 | }
42 | }
43 | ```
44 |
45 | The example above creates a map, then starts two goroutines which each have access to the map. One goroutine continuously mutates the values stored in the map, while the other prints the values it finds in the map.
46 |
47 | If we run the program on a multi-core machine, we get the following output: `fatal error: concurrent map iteration and map write`
48 |
49 | In Go, it isn’t safe to read from and write to a map at the same time.
50 |
51 | ## Mutexes to the rescue
52 |
53 | ```go
54 | package main
55 |
56 | import (
57 | "fmt"
58 | "sync"
59 | )
60 |
61 | func main() {
62 | m := map[int]int{}
63 |
64 | mux := &sync.Mutex{}
65 |
66 | go writeLoop(m, mux)
67 | go readLoop(m, mux)
68 |
69 | // stop program from exiting, must be killed
70 | block := make(chan struct{})
71 | <-block
72 | }
73 |
74 | func writeLoop(m map[int]int, mux *sync.Mutex) {
75 | for {
76 | for i := 0; i < 100; i++ {
77 | mux.Lock()
78 | m[i] = i
79 | mux.Unlock()
80 | }
81 | }
82 | }
83 |
84 | func readLoop(m map[int]int, mux *sync.Mutex) {
85 | for {
86 | mux.Lock()
87 | for k, v := range m {
88 | fmt.Println(k, "-", v)
89 | }
90 | mux.Unlock()
91 | }
92 | }
93 | ```
94 |
95 | In this example, we added a `sync.Mutex{}` and named it `mux`. In the write loop, the `Lock()` method is called before writing, and then the `Unlock()` is called when we're done. This Lock/Unlock sequence ensures that no other threads can `Lock()` the mutex while *we* have it locked – any other threads attempting to `Lock()` will block and wait until we `Unlock()`.
96 |
97 | In the reader, we `Lock()` before iterating over the map, and likewise `Unlock()` when we're done. Now the threads share the memory safely!
98 |
99 | > Mutex is short for [mutual exclusion](https://en.wikipedia.org/wiki/Mutual_exclusion), and the conventional name for the data structure that provides it is "mutex", often abbreviated to "mux".
100 |
101 | It's called "mutual exclusion" because a mutex *excludes* different threads (or goroutines) from accessing the same data at the same time.
102 |
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/mutexes-in-go.md:
--------------------------------------------------------------------------------
1 | # Mutexes in Go
2 |
3 | Mutexes allow us to *lock* access to data. This ensures that we can control which goroutines can access certain data at which time.
4 |
5 | Go's standard library provides a built-in implementation of a mutex with the [sync.Mutex](https://pkg.go.dev/sync#Mutex) type and its two methods:
6 |
7 | * [.Lock()](https://golang.org/pkg/sync/#Mutex.Lock)
8 | * [.Unlock()](https://golang.org/pkg/sync/#Mutex.Unlock)
9 |
10 | We can protect a block of code by surrounding it with a call to `Lock` and `Unlock` as shown on the `protected()` method below.
11 |
12 | It's good practice to structure the protected code within a function so that `defer` can be used to ensure that we never forget to unlock the mutex.
13 |
14 | ```go
15 | func protected(){
16 | mux.Lock()
17 | defer mux.Unlock()
18 | // the rest of the function is protected
19 | // any other calls to `mux.Lock()` will block
20 | }
21 | ```
22 |
23 | Mutexes are powerful. Like most powerful things, they can also cause many bugs if used carelessly.
24 |
25 | ## Maps are not thread-safe
26 |
27 | Maps are **not** safe for concurrent use! If you have multiple goroutines accessing the same map, and at least one of them is writing to the map, you must lock your maps with a mutex.
28 |
29 | > In reality, any Go code you write *may or may not* run on a single-core machine, so it's always best to write your code so that it is safe no matter which hardware it runs on.
30 |
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/parametric-constraints.md:
--------------------------------------------------------------------------------
1 | # Parametric Constraints
2 |
3 | Your interface definitions, which can later be used as constraints, can accept type parameters as well.
4 |
5 | ```go
6 | // The store interface represents a store that sells products.
7 | // It takes a type parameter P that represents the type of products the store sells.
8 | type store[P product] interface {
9 | Sell(P)
10 | }
11 |
12 | type product interface {
13 | Price() float64
14 | Name() string
15 | }
16 |
17 | type book struct {
18 | title string
19 | author string
20 | price float64
21 | }
22 |
23 | func (b book) Price() float64 {
24 | return b.price
25 | }
26 |
27 | func (b book) Name() string {
28 | return fmt.Sprintf("%s by %s", b.title, b.author)
29 | }
30 |
31 | type toy struct {
32 | name string
33 | price float64
34 | }
35 |
36 | func (t toy) Price() float64 {
37 | return t.price
38 | }
39 |
40 | func (t toy) Name() string {
41 | return t.name
42 | }
43 |
44 | // The bookStore struct represents a store that sells books.
45 | type bookStore struct {
46 | booksSold []book
47 | }
48 |
49 | // Sell adds a book to the bookStore's inventory.
50 | func (bs *bookStore) Sell(b book) {
51 | bs.booksSold = append(bs.booksSold, b)
52 | }
53 |
54 | // The toyStore struct represents a store that sells toys.
55 | type toyStore struct {
56 | toysSold []toy
57 | }
58 |
59 | // Sell adds a toy to the toyStore's inventory.
60 | func (ts *toyStore) Sell(t toy) {
61 | ts.toysSold = append(ts.toysSold, t)
62 | }
63 |
64 | // sellProducts takes a store and a slice of products and sells
65 | // each product one by one.
66 | func sellProducts[P product](s store[P], products []P) {
67 | for _, p := range products {
68 | s.Sell(p)
69 | }
70 | }
71 |
72 | func main() {
73 | bs := bookStore{
74 | booksSold: []book{},
75 | }
76 |
77 | // By passing in "book" as a type parameter, we can use the sellProducts function to sell books in a bookStore
78 | sellProducts[book](&bs, []book{
79 | {
80 | title: "The Hobbit",
81 | author: "J.R.R. Tolkien",
82 | price: 10.0,
83 | },
84 | {
85 | title: "The Lord of the Rings",
86 | author: "J.R.R. Tolkien",
87 | price: 20.0,
88 | },
89 | })
90 | fmt.Println(bs.booksSold)
91 |
92 | // We can then do the same for toys
93 | ts := toyStore{
94 | toysSold: []toy{},
95 | }
96 | sellProducts[toy](&ts, []toy{
97 | {
98 | name: "Lego",
99 | price: 10.0,
100 | },
101 | {
102 | name: "Barbie",
103 | price: 20.0,
104 | },
105 | })
106 | fmt.Println(ts.toysSold)
107 | }
108 | ```
109 |
110 | ## Naming Generic Types
111 |
112 | Let's look at this simple example again:
113 |
114 | ```go
115 | func splitAnySlice[T any](s []T) ([]T, []T) {
116 | mid := len(s)/2
117 | return s[:mid], s[mid:]
118 | }
119 | ```
120 |
121 | Remember, `T` is just a variable name, We could have named the type parameter *anything*. `T` happens to be a fairly common convention for a type variable, similar to how `i` is a convention for index variables in loops.
122 |
123 | This is just as valid:
124 |
125 | ```go
126 | func splitAnySlice[MyAnyType any](s []MyAnyType) ([]MyAnyType, []MyAnyType) {
127 | mid := len(s)/2
128 | return s[:mid], s[mid:]
129 | }
130 | ```
131 |
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/rw-mutex-review.md:
--------------------------------------------------------------------------------
1 | # Read - Write Mutex Review
2 |
3 | Maps are safe for concurrent *read* access, just not concurrent read/write or write/write access. A read/write mutex allows all the readers to access the map at the same time, but a writer will still lock out all other readers and writers.
4 |
5 | ```go
6 | package main
7 |
8 | import (
9 | "fmt"
10 | "sync"
11 | )
12 |
13 | func main() {
14 | m := map[int]int{}
15 |
16 | mux := &sync.RWMutex{}
17 |
18 | go writeLoop(m, mux)
19 | go readLoop(m, mux)
20 | go readLoop(m, mux)
21 | go readLoop(m, mux)
22 | go readLoop(m, mux)
23 |
24 | // stop program from exiting, must be killed
25 | block := make(chan struct{})
26 | <-block
27 | }
28 |
29 | func writeLoop(m map[int]int, mux *sync.RWMutex) {
30 | for {
31 | for i := 0; i < 100; i++ {
32 | mux.Lock()
33 | m[i] = i
34 | mux.Unlock()
35 | }
36 | }
37 | }
38 |
39 | func readLoop(m map[int]int, mux *sync.RWMutex) {
40 | for {
41 | mux.RLock()
42 | for k, v := range m {
43 | fmt.Println(k, "-", v)
44 | }
45 | mux.RUnlock()
46 | }
47 | }
48 | ```
49 |
50 | By using a `sync.RWMutex`, our program becomes *more efficient*. We can have as many `readLoop()` threads as we want, while still ensuring that the writers have exclusive access.
51 |
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/rw-mutex.md:
--------------------------------------------------------------------------------
1 | # RW Mutex
2 |
3 | The standard library also exposes a [sync.RWMutex](https://golang.org/pkg/sync/#RWMutex)
4 |
5 | In addition to these methods:
6 |
7 | - [Lock()](https://golang.org/pkg/sync/#Mutex.Lock)
8 | - [Unlock()](https://golang.org/pkg/sync/#Mutex.Unlock)
9 |
10 | The `sync.RWMutex` also has these methods:
11 |
12 | - [RLock()](https://golang.org/pkg/sync/#RWMutex.RLock)
13 | - [RUnlock()](https://golang.org/pkg/sync/#RWMutex.RUnlock)
14 |
15 | The `sync.RWMutex` can help with performance if we have a read-intensive process. Many goroutines can safely read from the map at the same time (multiple `Rlock()` calls can happen simultaneously). However, only one goroutine can hold a `Lock()` and all `RLock()`'s will also be excluded.
--------------------------------------------------------------------------------
/src/data/mutexes-and-generics/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Mutexes and Generics
4 |
5 | As your programs grow more complex and involve concurrent access to shared resources, ensuring data consistency and preventing race conditions becomes crucial. Go provides powerful tools to manage concurrent access, and generics add a layer of flexibility to your code.
6 |
7 | ### Mutexes in Go: Protecting Shared Resources
8 |
9 | Mutexes (mutual exclusion locks) are used to control access to shared data, ensuring that only one goroutine can modify the data at a time. This prevents race conditions and ensures data integrity.
10 |
11 | ```go
12 | var mutex sync.Mutex // Create a mutex
13 |
14 | // Locking
15 | mutex.Lock() // Acquire the lock
16 |
17 | // Access the shared data here
18 | mutex.Unlock() // Release the lock
19 | ```
20 |
21 | ### More on Mutexes: Understanding the Mechanics
22 |
23 | - Deadlock: Occurs when multiple goroutines are blocked waiting for each other to release locks, resulting in a standstill.
24 | - Lock Order: Consistent locking order is crucial for preventing deadlock, ensuring that goroutines always acquire locks in the same sequence.
25 | - Unlocking: Always unlock a mutex after accessing shared data, even if an error occurs.
26 |
27 | ### Read/Write Mutexes: Optimizing for Concurrent Access
28 |
29 | Read/write mutexes provide a more granular level of access control. They allow multiple readers to access the data concurrently, while only one writer is allowed to modify it at a time.
30 |
31 | ```go
32 | var rwMutex sync.RWMutex // Create a read/write mutex
33 |
34 | // Reading
35 | rwMutex.RLock() // Acquire the read lock
36 |
37 | // Access the shared data here
38 | rwMutex.RUnlock() // Release the read lock
39 |
40 | // Writing
41 | rwMutex.Lock() // Acquire the write lock
42 |
43 | // Modify the shared data here
44 | rwMutex.Unlock() // Release the write lock
45 | ```
46 |
47 | ### Generics in Go: Flexible Code with Type Parameters
48 |
49 | Generics enable you to write functions and data structures that can work with different data types without requiring explicit type specifications. This promotes code reusability and maintainability.
50 |
51 | ```go
52 | func sum[T int | float64](values []T) T {
53 | total := values[0]
54 | for _, value := range values[1:] {
55 | total += value
56 | }
57 | return total
58 | }
59 |
60 | intSum := sum([]int{1, 2, 3}) // Sum of integers
61 | floatSum := sum([]float64{1.5, 2.5, 3.5}) // Sum of floats
62 | ```
63 |
64 | ### Constraints: Specifying Type Requirements
65 |
66 | Constraints define the types that a generic function or data structure can accept. They enforce type safety and ensure that your code works correctly with the specified types.
67 |
68 | ```go
69 | func sum[T constraints.Ordered](values []T) T {
70 | // ... implementation ...
71 | }
72 | ```
73 |
74 | ### Parametric Constraints: Specifying Relationships
75 |
76 | Parametric constraints enable you to define relationships between type parameters. This allows you to create more flexible and expressive generics.
77 |
78 | ```go
79 | func equal[T comparable](a, b T) bool {
80 | return a == b
81 | }
82 | ```
83 |
--------------------------------------------------------------------------------
/src/data/setting-up-environment/choose-your-tools.md:
--------------------------------------------------------------------------------
1 | # Choose Your Tools
2 |
3 | While you wrote a small Go program using nothing more than a text editor and the go command, you’ll probably want more advanced tools when working on larger projects. Go IDEs provide many advantages over text editors, including automatic formatting on save, code completion, type checking, error reporting, and integrated debugging. If you don’t already have a favorite tool, two of the most popular Go development environments are **Visual Studio** Code and **GoLand**.
4 |
5 | ## Visual Studio Code
6 |
7 | If you are looking for a free development environment, Visual Studio Code from Microsoft is your best option. Since it was released in 2015, VS Code has become the most popular source code editor for developers. It does not ship with Go support, but you can make it a Go development environment by downloading the Go extension from the extensions gallery. VS Code’s Go support relies on third-party extensions that are accessed via its built-in Marketplace. This includes the Go Development tools, the Delve debugger, and gopls, a Go language server developed by the Go team. While you need to install the Go compiler yourself, the Go extension will install Delve and gopls for you.
8 |
9 | > What is a language server? It’s a standard specification for an API that enables editors to implement intelligent editing behavior, like code completion, quality checks, or finding all the places a variable or function is used in your code.
10 |
11 | ## GoLand
12 |
13 | GoLand is the Go-specific IDE from JetBrains. While JetBrains is best known for Java-centric tools, GoLand is an excellent Go development environment. GoLand’s user interface looks similar to IntelliJ, PyCharm, RubyMine, WebStorm, Android Studio, or any of the other JetBrains IDEs. Its Go support includes refactoring, syntax highlighting, code completion and navigation, documentation pop-ups, a debugger, code coverage, and more. In addition to Go support, GoLand includes JavaScript/HTML/CSS and SQL database tools. Unlike VS Code, GoLand doesn’t require you to install a plug-in to get it to work. If you have already subscribed to IntelliJ Ultimate, you can add Go support via a plug-in. While GoLand is commercial software, JetBrains has a Free License Program for students and core open source contributors. If you don’t qualify for a free license, a 30-day free trial is available. After that, you have to pay for GoLand.
14 |
15 | ## The Go Playground
16 |
17 | There’s one more important tool for Go development, but this is one that you don’t install. Visit The [Go Playground](https://go.dev/play) and you’ll see a window that provides a quick editor for Go. If you have used a command-line environment like irb, node, or python, you’ll find the Go Playground has a similar feel. It gives you a place to try out and share small programs. Enter your program into the window and click the Run button to execute the code. The Format button runs go fmt on your program and updates your imports. The Share button creates a unique URL that you can send to someone else to take a look at your program or to come back to your code at a future date (the URLs have proven to be persistent for a long time, but I wouldn’t rely on the playground as your source code repository).
18 |
19 | > Do not put sensitive information (such as personally identifiable information, passwords, or private keys) into your playground! If you click the Share button, the information is saved on Google’s servers and is accessible to anyone who has the associated Share URL. If you do this by accident, contact Google at security@golang.org with the URL and the reason the content needs to be removed.
--------------------------------------------------------------------------------
/src/data/setting-up-environment/go-tooling.md:
--------------------------------------------------------------------------------
1 | # Go Tooling
2 |
3 | All of the Go development tools are accessed via the go command. In addition to `go version` and `go run`, there’s a compiler (`go build`), code formatter (`go fmt`), dependency manager (`go mod`), test runner (`go test`), a tool that scans for common coding mistakes (`go vet`), and more.
4 |
5 | ## Go Build
6 |
7 | `go build` compiles go code into an executable program. Ensure you are in your hellogo repo, then run:
8 |
9 | ```bash
10 | go build
11 | ```
12 |
13 | Run the new program:
14 |
15 | ```bash
16 | ./hellogo
17 | ```
18 |
19 | ## Go Install
20 |
21 | Ensure you are in your `hellogo` repo, then run:
22 |
23 | ```bash
24 | go install
25 | ```
26 |
27 | Navigate out of your project directory:
28 |
29 | ```bash
30 | cd ../
31 | ```
32 |
33 | Go has installed the `hellogo` program globally. Run it with:
34 |
35 | ```bash
36 | hellogo
37 | ```
38 |
39 | > If you get an error regarding "hellogo not found" it means you probably don't have your Go environment setup properly. Specifically, `go install` is adding your binary to your `GOBIN` directory, but that may not be in your `PATH`. You can read more about that here in the [go install docs](https://pkg.go.dev/cmd/go#hdr-Compile_and_install_packages_and_dependencies).
40 |
41 | ## Go fmt
42 |
43 | One of the chief design goals for Go was to create a language that allowed you to write code efficiently. This meant having simple syntax and a fast compiler. It also led Go’s authors to reconsider code formatting. Most languages allow a great deal of flexibility in the way code is formatted. Go does not. Enforcing a standard format makes it a great deal easier to write tools that manipulate source code. This simplifies the compiler and allows the creation of some clever tools for generating code. There is a secondary benefit as well. Developers have historically wasted extraordinary amounts of time on format wars. Since Go defines a standard way of formatting code, Go developers avoid arguments over brace style and tabs versus spaces. For example, Go programs use tabs to indent, and it is a syntax error if the opening brace is not on the same line as the declaration or command that begins the block.
44 |
45 | > Many Go developers think the Go team defined a standard format as a way to avoid developer arguments and discovered the tooling advantages later. However, Russ Cox, the development lead for Go, has publicly stated that better tooling was his original motivation.
46 |
47 | The Go development tools include a command, `go fmt`, which automatically fixes the whitespace in your code to match the standard format. However, it can’t fix braces on the wrong line. Run it with the following:
48 |
49 | ```bash
50 | go fmt ./...
51 | ```
52 |
53 | Using `./...` tells a Go tool to apply the command to all the files in the current
54 | directory and all subdirectories
55 |
56 | > Remember to run `go fmt` before you compile your code, and, at the very least, before you commit source code changes to your repository! If you forget, make a separate commit that does only `go fmt ./...` so you don’t hide logic changes in an avalanche of formatting changes.
57 |
--------------------------------------------------------------------------------
/src/data/setting-up-environment/installing-go-tools.md:
--------------------------------------------------------------------------------
1 | # Installing Go Tools
2 |
3 | To build Go code, you need to download and install the Go development tools. You
4 | can find the latest version of the tools at the downloads page on the [Go website](https://go.dev/dl). Choose the download for your platform and install it. The `.pkg` installer for Mac
5 | and the `.msi` installer for Windows automatically install Go in the correct location,
6 | remove any old installations, and put the Go binary in the default executable path. If you are on a Mac, you can install Go using [Homebrew](https://brew.sh) with the command
7 |
8 | ```bash
9 | brew install go
10 | ```
11 |
12 | Windows developers who use [Chocolatey](https://chocolatey.org) can install Go with the command
13 |
14 | ```bash
15 | choco install golang
16 | ```
17 |
18 | The various Linux and BSD installers are gzipped TAR files and expand to a directory named go. Copy this directory to `/usr/local` and add `/usr/local/go/bin` to your `$PATH` so that the go command is accessible:
19 |
20 | ```bash
21 | $ tar -C /usr/local -xzf go1.20.5.linux-amd64.tar.gz
22 | $ echo 'export PATH=$PATH:/usr/local/go/bin' >> $HOME/.bash_profile
23 | $ source $HOME/.bash_profile
24 | ```
25 |
26 | You might need root permissions to write to /usr/local. If the tar command fails, rerun it with sudo tar -C /usr/local -xzf go1.20.5.linux-amd64.tar.gz. You can validate that your environment is set up correctly by opening up a terminal or command prompt and typing:
27 |
28 | ```bash
29 | $ go version
30 | ```
31 |
32 | If everything is set up correctly, you should see something like this printed:
33 |
34 | ```bash
35 | go version go1.20.5 darwin/arm64
36 | ```
37 |
38 | This tells you that this is Go version 1.20.5 on macOS. (Darwin is the operating system at the heart of macOS, and arm64 is the name for the 64-bit chips based on ARM’s designs.) On x64 Linux, you would see:
39 |
40 | ```bash
41 | go version go1.20.5 linux/amd64
42 | ```
43 |
44 | ## Troubleshooting Your Go Installation
45 |
46 | If you get an error instead of the version message, it’s likely that you don’t have go in your executable path, or you have another program named go in your path. On macOS and other Unix-like systems, use `which go` to see the go command being executed, if any. If nothing is returned, you need to fix your executable path. If you’re on Linux or BSD, it’s possible you installed the 64-bit Go development tools
47 | on a 32-bit system or the development tools for the wrong chip architecture.
48 |
49 | > Go programs compile to a single native binary and do not require any additional software to be installed in order to run them. This is in contrast to languages like Java, Python, and JavaScript, which require you to install a virtual machine to run your program. Using a single native binary makes it a lot easier to distribute programs written in Go. Developers who use Docker or Kubernetes can often package a Go app inside a scratch or distroless image.
50 |
51 | This guide will assume you are on a Unix environment like Linux or Mac. If you're on Windows you may have to use `powershell` or do just a *bit* of Google-ing or ask in Discord to figure out how some commands translate to Windows.
52 | If you are on Windows, I'd optionally recommend checking out [WSL (Windows Subsystem for Linux)](https://docs.microsoft.com/en-us/windows/wsl/install) so that you can work in a Unix environment on your local machine.
53 |
54 | > Make sure to use at least Go version `1.20`.
55 |
--------------------------------------------------------------------------------
/src/data/setting-up-environment/modules.md:
--------------------------------------------------------------------------------
1 | # Modules
2 |
3 | Go programs are organized into *packages*. A package is a directory of Go code that's all compiled together. Functions, types, variables, and constants defined in one source file are visible to **all other source files within the same package (directory)**. A *repository* contains one or more *modules*. A module is a collection of Go packages that are released together.
4 |
5 | ## Go Modules
6 |
7 | A Go repository typically contains only one module, located at the root of the repository. A file named `go.mod` at the root of a project declares the module. It contains:
8 |
9 | - The module path
10 | - The version of the Go language your project requires
11 | - Optionally, any external package dependencies your project has
12 |
13 | The module path is just the import path prefix for all packages within the module. Here's an example of a `go.mod` file:
14 |
15 | ```go
16 | module github.com/AmanuelCh/linkpreview
17 |
18 | go 1.22
19 |
20 | require github.com/google/examplepackage v1.3.0
21 | ```
22 |
23 | Each module's path not only serves as an import path prefix for the packages within but *also indicates where the go command should look to download it*. For example, to download the module `golang.org/x/tools`, the go command would consult the repository located at [https://golang.org/x/tools](https://golang.org/x/tools).
24 |
25 | > An "import path" is a string used to import a package. A package's import path is its module path joined with its subdirectory within the module. For example, the module `github.com/google/go-cmp` contains a package in the directory `cmp/`. That package's import path is `github.com/google/go-cmp/cmp`. Packages in the standard library do not have a module path prefix.
26 |
27 | ## Do I Need to Put my Package on GitHub?
28 |
29 | You don't *need* to publish your code to a remote repository before you can build it. A module can be defined locally without belonging to a repository. However, it's a good habit to keep a copy of all your projects on a remote server, like GitHub.
30 |
31 | ## Go Path
32 |
33 | Your machine will contain many version control *repositories* (managed by Git, for example). Each repository contains one or more *packages*, but will typically be a single *module*. Each package consists of one or more *Go source files* in a single directory. The path to a package's directory determines its *import path* and where it can be downloaded from if you decide to host it on a remote version control system like Github or Gitlab.
34 |
35 | > The `$GOPATH` environment variable will be set by default somewhere on your machine (typically in the home directory, `~/go`). Since we will be working in the new "Go modules" setup, you *don't need to worry about that*. If you read something online about setting up your `GOPATH`, that documentation is probably out of date. These days you should *avoid* working in the `$GOPATH/src` directory. Again, that's the old way of doing things and can cause unexpected issues, so better to just avoid it.
36 |
37 | ## Get into your Workspace
38 |
39 | Navigate to a location on your machine where you want to store some code. For example, I store all my code in `~/workspace`, then organize it into subfolders based on the remote location. For example,
40 |
41 | > `~/workspace/github.com/AmanuelCh/linkpreview` = [https://github.com/AmanuelCh/linkpreview](https://github.com/AmanuelCh/linkpreview)
42 |
43 | That said, you can put your code wherever you want.
--------------------------------------------------------------------------------
/src/data/setting-up-environment/packages.md:
--------------------------------------------------------------------------------
1 | # Packages
2 |
3 | Every Go program is made up of packages. You have probably noticed the `package main` at the top of the `hello world` program from the [Intro](/intro) section. A package named "main" has an entrypoint at the `main()` function. A `main` package is compiled into an executable program. A package by any other name is a "library package". Libraries have no entry point. Libraries simply export functionality that can be used by other packages. For example:
4 |
5 | ```go
6 | package main
7 |
8 | import (
9 | "fmt"
10 | "math/rand"
11 | )
12 |
13 | func main() {
14 | fmt.Println("My favorite number is", rand.Intn(10))
15 | }
16 | ```
17 |
18 | This program is an executable. It is a "main" package and *imports* from the `fmt` and `math/rand` library packages.
19 |
20 | ## Package Naming
21 |
22 | By *convention*, a package's name is the same as the last element of its import path. For instance, the `math/rand` package comprises files that begin with:
23 |
24 | ```go
25 | package rand
26 | ```
27 |
28 | That said, package names aren't *required* to match their import path. For example, you could write a new package with the path `github.com/mailio/rand` and name the package `random`:
29 |
30 | ```go
31 | package random
32 | ```
33 |
34 | While the above is possible, it is discouraged for the sake of consistency.
35 |
36 | ## One Package / Directory
37 |
38 | A directory of Go code can have **at most** one package. All `.go` files in a single directory must all belong to the same package. If they don't an error will be thrown by the compiler. This is true for main and library packages alike.
39 |
--------------------------------------------------------------------------------
/src/data/setting-up-environment/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | Ready to write your first Go program on your local machine? We have covered how to do so. Here's a quick recap:
4 |
5 | ## Installation: The Gateway to Go
6 |
7 | Installing Go is a breeze. Here are a few ways to get it up and running:
8 |
9 | - Official Website: The official Go website ([https://golang.org/](https://golang.org/)) provides clear installation instructions for your operating system. It's the easiest and most reliable way to get started.
10 | - Package Managers (Mac & Linux): You can also install Go using package managers like `brew` (macOS), `apt` (Debian/Ubuntu), or `yum` (Red Hat/CentOS). These tools simplify the installation process.
11 |
12 | ### Essential Tools: Your Go Toolbox
13 |
14 | Once Go is installed, you'll want to familiarize yourself with some essential tools:
15 |
16 | - `go fmt`: This command automatically formats your Go code, ensuring consistent style and readability.
17 | - `go run`: Run your Go code directly from the command line. This is great for quick testing and experimentation.
18 | - `go build`: Compile your Go code into an executable file, allowing you to distribute and run your program on different machines.
19 | - `go get`: Install and manage Go packages and modules (which are collections of reusable code).
20 |
21 | ### A Package of Power: Modules and Packages
22 |
23 | Go's module system is a game-changer for managing dependencies. Modules are collections of Go packages that provide reusable functionality. Here's why they are so important:
24 |
25 | - Organization: Modules help organize your code into logical units, making it easier to manage and maintain large projects.
26 | - Dependency Management: Modules allow you to easily include and update libraries of code created by others, saving you time and effort.
27 |
28 | ### Your Coding Canvas: Text Editors and IDEs
29 |
30 | Go works well with various text editors and integrated development environments (IDEs).
31 |
32 | - VS Code: A highly popular and customizable editor with excellent Go extensions that provide features like syntax highlighting, code completion, and debugging.
33 | - GoLand: A powerful IDE specifically designed for Go development, providing comprehensive features like code navigation, testing, and refactoring.
34 | - Go Playground: A fantastic online environment where you can write and run Go code without any installation. It's a great place to experiment and learn.
35 |
36 | ### Ready to Code?
37 |
38 | You've now got your Go environment ready to go. Don't hesitate to explore these tools, experiment, and dive into the world of Go. From here, you can tackle variables, data types, and more. Happy coding!
39 |
--------------------------------------------------------------------------------
/src/data/setting-up-environment/your-first-go-program.md:
--------------------------------------------------------------------------------
1 | # Your First Go Program
2 |
3 | Once inside your personal workspace, create a new directory and enter it:
4 |
5 | ```bash
6 | mkdir hellogo
7 | cd hellogo
8 | ```
9 |
10 | Inside the directory declare your module's name:
11 |
12 | ```bash
13 | go mod init {REMOTE}/{USERNAME}/hellogo
14 | ```
15 |
16 | Where `{REMOTE}` is your preferred remote source provider (i.e. `github.com`) and `{USERNAME}` is your Git username. If you don't use a remote provider yet, just use `example.com/username/hellogo`. Print your `go.mod` file:
17 |
18 | ```bash
19 | cat go.mod
20 | ```
21 |
22 | Inside `hellogo`, create a new file called `main.go`. Conventionally, the file in the `main` package that contains the `main()` function is called `main.go`. Paste the following code into your file:
23 |
24 | ```go
25 | package main
26 |
27 | import "fmt"
28 |
29 | func main() {
30 | fmt.Println("hello world")
31 | }
32 | ```
33 |
34 | Run the code
35 |
36 | ```bash
37 | go run main.go
38 | ```
39 |
40 | The `go run` command is used to quickly compile and run a Go package. The compiled binary is *not* saved in your working directory. Use `go build` instead to compile production executables. I rarely use `go run` other than to quickly do some testing or debugging.
41 |
42 | ## Further Reading
43 |
44 | Execute `go help run` in your shell and read the instructions.
--------------------------------------------------------------------------------
/src/data/slices/arrays.md:
--------------------------------------------------------------------------------
1 | # Arrays in Go
2 |
3 | Like most programming languages, Go has arrays. However, arrays are rarely used directly in Go. All elements in the array must be of the type that’s specified. There are a few declaration styles. In the first, you specify the size of the array and the type of the elements in the array:
4 |
5 | ```go
6 | var x [3]int
7 | ```
8 |
9 | This creates an array of three ints. Since no values were specified, all of the elements (x[0], x[1], and x[2]) are initialized to the zero value for an int, which is (of course) 0. If you have initial values for the array, you specify them with an array literal:
10 |
11 | ```go
12 | var x = [3]int{10, 20, 30}
13 | ```
14 |
15 | If you have a sparse array (an array where most elements are set to their zero value), you can specify only the indices with nonzero values in the array literal:
16 |
17 | ```go
18 | var x = [12]int{1, 5: 4, 6, 10: 100, 15}
19 | ```
20 |
21 | This creates an array of 12 ints with the following values: [1, 0, 0, 0, 0, 4, 6, 0, 0, 0, 100, 15]
22 |
23 | ## Don't Use Arrays Directly
24 |
25 | We said that arrays in Go are rarely used explicitly. This is because they come with an unusual limitation: Go considers the size of the array to be part of the type of the array. This makes an array that’s declared to be [3]int a different type from an array that’s declared to be [4]int. This also means that you cannot use a variable to specify the size of an array, because types must be resolved at compile time, not at runtime. What’s more, you can’t use a type conversion to directly convert arrays of different sizes to identical types. Because you can’t convert arrays of different sizes into each other, you can’t write a function that works with arrays of any size and you can’t assign arrays of different sizes to the same variable.
26 |
27 | > Because of these restrictions, don’t use arrays unless you know the exact length you need ahead of time. For example, some of the cryptographic functions in the standard library return arrays because the sizes of checksums are defined as part of the algorithm. This is the exception, not the rule. This raises the question: why is such a limited feature in the language? The main reason arrays exist in Go is to provide the backing store for slices, which are one of the most useful features of Go.
28 |
--------------------------------------------------------------------------------
/src/data/slices/len-and-cap-review.md:
--------------------------------------------------------------------------------
1 | # Len and Cap Review
2 |
3 | The length of a slice may be changed as long as it still fits within the limits of the underlying array; just assign it to a slice of itself. The *capacity* of a slice, accessible by the built-in function `cap`, reports the maximum length the slice may assume. Here is a function to append data to a slice. If the data exceeds the capacity, the slice is reallocated. The resulting slice is returned. The function uses the fact that `len` and `cap` are legal when applied to the `nil` slice, and return `0`.
4 |
5 | Referenced from [Effective Go](https://golang.org/doc/effective_go.html#slices)
6 |
7 | ```go
8 | func Append(slice, data []byte) []byte {
9 | l := len(slice)
10 | if l + len(data) > cap(slice) { // reallocate
11 | // Allocate double what's needed, for future growth.
12 | newSlice := make([]byte, (l+len(data))*2)
13 | // The copy function is predeclared and works for any slice type.
14 | copy(newSlice, slice)
15 | slice = newSlice
16 | }
17 | slice = slice[0:l+len(data)]
18 | copy(slice[l:], data)
19 | return slice
20 | }
21 | ```
22 |
--------------------------------------------------------------------------------
/src/data/slices/make-function.md:
--------------------------------------------------------------------------------
1 | # Make Function
2 |
3 | Most of the time we don't need to think about the underlying array of a slice. We can create a new slice using the `make` function:
4 |
5 | ```go
6 | // func make([]T, len, cap) []T
7 | mySlice := make([]int, 5, 10)
8 |
9 | // the capacity argument is usually omitted and defaults to the length
10 | mySlice := make([]int, 5)
11 | ```
12 |
13 | Slices created with `make` will be filled with the zero value of the type. If we want to create a slice with a specific set of values, we can use a slice literal:
14 |
15 | ```go
16 | mySlice := []string{"I", "love", "go"}
17 | ```
18 |
19 | Note that the array brackets *do not* have a `3` in them. If they did, you'd have an *array* instead of a slice.
20 |
21 | ## Length
22 |
23 | The length of a slice is simply the number of elements it contains. It is accessed using the built-in `len()` function:
24 |
25 | ```go
26 | mySlice := []string{"I", "love", "go"}
27 | fmt.Println(len(mySlice)) // 3
28 | ```
29 |
30 | ## Capacity
31 |
32 | The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice. It is accessed using the built-in `cap()` function:
33 |
34 | ```go
35 | mySlice := []string{"I", "love", "go"}
36 | fmt.Println(cap(mySlice)) // 3
37 | ```
38 |
39 | Generally speaking, unless you're hyper-optimizing the memory usage of your program, you don't need to worry about the capacity of a slice because it will automatically grow as needed.
40 |
--------------------------------------------------------------------------------
/src/data/slices/slice-gotcha.md:
--------------------------------------------------------------------------------
1 | # Tricky Slices
2 |
3 | The `append()` function changes the underlying array of its parameter AND returns a new slice. This means that using `append()` on anything other than itself is usually a BAD idea.
4 |
5 | ```go
6 | // dont do this!
7 | someSlice = append(otherSlice, element)
8 | ```
9 |
10 | Take a look at these head-scratchers:
11 |
12 | ## Example 1: Works as expected
13 |
14 | ```go
15 | a := make([]int, 3)
16 | fmt.Println("len of a:", len(a))
17 | // len of a: 3
18 | fmt.Println("cap of a:", cap(a))
19 | // cap of a: 3
20 | fmt.Println("appending 4 to b from a")
21 | // appending 4 to b from a
22 | b := append(a, 4)
23 | fmt.Println("b:", b)
24 | // b: [0 0 0 4]
25 | fmt.Println("addr of b:", &b[0])
26 | // addr of b: 0x44a0c0
27 | fmt.Println("appending 5 to c from a")
28 | // appending 5 to c from a
29 | c := append(a, 5)
30 | fmt.Println("addr of c:", &c[0])
31 | // addr of c: 0x44a180
32 | fmt.Println("a:", a)
33 | // a: [0 0 0]
34 | fmt.Println("b:", b)
35 | // b: [0 0 0 4]
36 | fmt.Println("c:", c)
37 | // c: [0 0 0 5]
38 | ```
39 |
40 | With slices `a`, `b`, and `c`, `4` and `5` seem to be appended as we would expect. We can even check the memory addresses and confirm that `b` and `c` point to different underlying arrays.
41 |
42 | ## Example 2: Something fishy
43 |
44 | ```go
45 | i := make([]int, 3, 8)
46 | fmt.Println("len of i:", len(i))
47 | // len of i: 3
48 | fmt.Println("cap of i:", cap(i))
49 | // cap of i: 8
50 | fmt.Println("appending 4 to j from i")
51 | // appending 4 to j from i
52 | j := append(i, 4)
53 | fmt.Println("j:", j)
54 | // j: [0 0 0 4]
55 | fmt.Println("addr of j:", &j[0])
56 | // addr of j: 0x454000
57 | fmt.Println("appending 5 to g from i")
58 | // appending 5 to g from i
59 | g := append(i, 5)
60 | fmt.Println("addr of g:", &g[0])
61 | // addr of g: 0x454000
62 | fmt.Println("i:", i)
63 | // i: [0 0 0]
64 | fmt.Println("j:", j)
65 | // j: [0 0 0 5]
66 | fmt.Println("g:", g)
67 | // g: [0 0 0 5]
68 | ```
69 |
70 | In this example however, when `5` is appended to `g` it overwrites `j`'s fourth index because `j` and `g` point to the *same underlying array*. The `append()` function only creates a new array when there isn't any capacity left. We created `i` with a length of 3 and a capactiy of 8, which means we can append `5` items before a new array is automatically allocated. Again, to avoid bugs like this, you should always use the `append` function on the same slice the result is assigned to:
71 |
72 | ```go
73 | mySlice := []int{1, 2, 3}
74 | mySlice = append(mySlice, 4)
75 | ```
76 |
--------------------------------------------------------------------------------
/src/data/slices/slices-in-go.md:
--------------------------------------------------------------------------------
1 | # Slices in Go
2 |
3 | *99 times out of 100* you will use a slice instead of an array when working with ordered lists. Arrays are fixed in size. Once you make an array like `[10]int` you can't add an 11th element. A slice is a *dynamically-sized*, *flexible* view of the elements of an array. Slices **always** have an underlying array, though it isn't always specified explicitly. To explicitly create a slice on top of an array we can do:
4 |
5 | ```go
6 | primes := [6]int{2, 3, 5, 7, 11, 13}
7 | mySlice := primes[1:4]
8 | // mySlice = {3, 5, 7}
9 | ```
10 |
11 | The syntax is:
12 |
13 | ```go
14 | arrayname[lowIndex:highIndex]
15 | arrayname[lowIndex:]
16 | arrayname[:highIndex]
17 | arrayname[:]
18 | ```
19 |
20 | Where `lowIndex` is inclusive and `highIndex` is exclusive. Either `lowIndex` or `highIndex` or both can be omitted to use the entire array on that side.
21 |
22 | > What makes slices so useful is that you can grow slices as needed. This is because the length of a slice is not part of its type. This removes the biggest limitations of arrays and allows you to write a single function that processes slices of any size.
23 |
--------------------------------------------------------------------------------
/src/data/slices/slices-review.md:
--------------------------------------------------------------------------------
1 | # Slices Review
2 |
3 | Slices wrap arrays to give a more general, powerful, and convenient interface to sequences of data. Except for items with explicit dimensions such as transformation matrices, most array programming in Go is done with slices rather than simple arrays.
4 |
5 | ### Slices hold references
6 |
7 | Slices hold references to an underlying array, and if you assign one slice to another, both refer to the **same** array. If a function takes a slice argument, changes it makes to the elements of the slice *will be visible to the caller*, analogous to passing a pointer to the underlying array. A Read function can therefore accept a slice argument rather than a pointer and a count; the length within the slice sets an upper limit of how much data to read. Here is the signature of the [Read()](https://pkg.go.dev/os#File.Read) method of the `File` type in package `os`:
8 |
9 | Referenced from [Effective Go](https://golang.org/doc/effective_go.html#slices)
10 |
11 | ```go
12 | func (f *File) Read(buf []byte) (n int, err error)
13 | ```
14 |
15 | ### Can we compare slices ?
16 |
17 | It is a compile-time error to use `==` to see if two slices are identical or `!=` to see if they are different. The only thing you can compare a slice with using `==` is `nil`:
18 |
19 | ```go
20 | var x []int //x is assigned the zero value for a slice
21 |
22 | fmt.Println(x == nil) // prints true
23 | ```
24 |
25 | Since Go 1.21, the slices package in the standard library includes two functions to compare slices. The `slices.Equal` function takes in two slices and returns true if the slices are the same length, and all of the elements are equal. It requires the elements of the slice to be comparable. The other function, `slices.EqualFunc`, lets you pass in a function to determine equality and does not require the slice elements to be comparable.
26 |
--------------------------------------------------------------------------------
/src/data/slices/variadic-functions.md:
--------------------------------------------------------------------------------
1 | # Variadic Functions
2 |
3 | Many functions, especially those in the standard library, can take an arbitrary number of *final* arguments. This is accomplished by using the "`...`" syntax in the function signature. A variadic function receives the variadic arguments as a slice.
4 |
5 | ```go
6 | func sum(nums ...int) int {
7 | // nums is just a slice
8 | for i := 0; i < len(nums); i++{
9 | num := nums[i]
10 | }
11 | }
12 |
13 | func main() {
14 | total := sum(1, 2, 3)
15 | fmt.Println(total)
16 | // prints "6"
17 | }
18 | ```
19 |
20 | The familiar [fmt.Println()](https://pkg.go.dev/fmt#Println) and [fmt.Sprintf()](https://pkg.go.dev/fmt#Sprintf) are variadic! `fmt.Println()` prints each element with space [delimiters](https://www.dictionary.com/browse/delimited) and a newline at the end.
21 |
22 | ```go
23 | func Println(a ...interface{}) (n int, err error)
24 | ```
25 |
26 | ## Spread Operator
27 |
28 | The spread operator allows us to pass a slice *into* a variadic function. The spread operator consists of three dots following the slice in the function call.
29 |
30 | ```go
31 | func printStrings(strings ...string) {
32 | for i := 0; i < len(strings); i++ {
33 | fmt.Println(strings[i])
34 | }
35 | }
36 |
37 | func main() {
38 | names := []string{"bob", "sue", "alice"}
39 | printStrings(names...)
40 | }
41 | ```
42 |
43 | ## Append
44 |
45 | The built-in append function is used to dynamically add elements to a slice:
46 |
47 | ```go
48 | func append(slice []Type, elems ...Type) []Type
49 | ```
50 |
51 | If the underlying array is not large enough, `append()` will create a new underlying array and point the slice to it. Notice that `append()` is variadic, the following are all valid:
52 |
53 | ```go
54 | slice = append(slice, oneThing)
55 | slice = append(slice, firstThing, secondThing)
56 | slice = append(slice, anotherSlice...)
57 | ```
58 |
59 | ## Slice of Slices
60 |
61 | Slices can hold other slices, effectively creating a [matrix](https://en.wikipedia.org/wiki/Matrix_(mathematics)), or a 2D slice.
62 |
63 | ```go
64 | rows := [][]int{}
65 | ```
66 |
67 | ## Range
68 |
69 | Go provides syntactic sugar to iterate easily over elements of a slice:
70 |
71 | ```go
72 | for INDEX, ELEMENT := range SLICE {
73 | }
74 | ```
75 |
76 | For example:
77 |
78 | ```go
79 | fruits := []string{"apple", "banana", "grape"}
80 | for i, fruit := range fruits {
81 | fmt.Println(i, fruit)
82 | }
83 | // 0 apple
84 | // 1 banana
85 | // 2 grape
86 | ```
87 |
--------------------------------------------------------------------------------
/src/data/slices/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Slicing and Dicing Your Data
4 |
5 | Arrays and slices are fundamental data structures in Go, providing flexible ways to store and manipulate collections of data.
6 |
7 | ### Arrays: Fixed-Size Collections
8 |
9 | Arrays are fixed-size collections that hold elements of the same data type. They are declared with a specific length, and you cannot change that length after creation.
10 |
11 | ```go
12 | var numbers [5]int // An array of 5 integers
13 | numbers[0] = 1
14 | numbers[1] = 2
15 | numbers[2] = 3
16 | numbers[3] = 4
17 | numbers[4] = 5
18 | ```
19 |
20 | ### Slices: Dynamic and Flexible
21 |
22 | Slices are dynamic, resizable segments of arrays. They provide a more flexible way to work with collections, allowing you to add, remove, and resize elements on the fly.
23 |
24 | ```go
25 | letters := []string{"a", "b", "c"} // A slice of strings
26 | ```
27 |
28 | ### The `make` Function: Creating Slices
29 |
30 | The `make` function is used to create slices, specifying their initial length and capacity.
31 |
32 | ```go
33 | numbers := make([]int, 5, 10) // Create a slice with length 5 and capacity 10
34 | ```
35 |
36 | - Length: The number of elements currently in the slice.
37 | - Capacity: The total number of elements the slice can hold without reallocation.
38 |
39 | ### Variadic Functions: Handling Variable Number of Arguments
40 |
41 | Variadic functions accept a variable number of arguments of the same type. This is useful for functions that need to work with an unknown number of inputs.
42 |
43 | ```go
44 | func sum(numbers ...int) int {
45 | total := 0
46 | for _, number := range numbers {
47 | total += number
48 | }
49 | return total
50 | }
51 |
52 | result := sum(1, 2, 3, 4, 5) // Pass any number of arguments
53 | ```
54 |
55 | ### `len` and `cap`: Getting Length and Capacity
56 |
57 | The `len` function returns the length of a slice, and the `cap` function returns its capacity.
58 |
59 | ```go
60 | numbers := make([]int, 5, 10)
61 | fmt.Println(len(numbers)) // Output: 5
62 | fmt.Println(cap(numbers)) // Output: 10
63 | ```
64 |
65 | ### Slice Gotchas: Avoiding Common Pitfalls
66 |
67 | - Slices are References: Slices are references to underlying arrays. Changes made to a slice affect the underlying array.
68 | - Slice Resizing: When a slice grows beyond its capacity, Go will automatically reallocate memory, potentially copying the data to a new location.
69 | - Slice Appending: The `append` function adds elements to a slice, resizing it as needed.
70 |
--------------------------------------------------------------------------------
/src/data/structs/anonymous-structs.md:
--------------------------------------------------------------------------------
1 | # Anonymous Structs
2 |
3 | An anonymous struct is just like a normal struct, but it is defined without a name and therefore cannot be referenced elsewhere in the code. To create an anonymous struct, just instantiate the instance immediately using a second pair of brackets after declaring the type:
4 |
5 | ```go
6 | myCar := struct {
7 | Make string
8 | Model string
9 | } {
10 | Make: "tesla",
11 | Model: "model 3"
12 | }
13 | ```
14 |
15 | You can even nest anonymous structs as fields within other structs:
16 |
17 | ```go
18 | type car struct {
19 | Make string
20 | Model string
21 | Height int
22 | Width int
23 | // Wheel is a field containing an anonymous struct
24 | Wheel struct {
25 | Radius int
26 | Material string
27 | }
28 | }
29 | ```
30 |
31 | ## When Should you use an Anonymous struct?
32 |
33 | In general, *prefer named structs*. Named structs make it easier to read and understand your code, and they have the nice side-effect of being reusable. I sometimes use anonymous structs when I *know* I won't ever need to use a struct again. For example, sometimes I'll use one to create the shape of some JSON data in HTTP handlers. If a struct is only meant to be used once, then it makes sense to declare it in such a way that developers down the road won’t be tempted to accidentally use it again. You can read more about [anonymous structs here](https://www.willem.dev/articles/anonymous-structs/) if you're curious.
34 |
--------------------------------------------------------------------------------
/src/data/structs/embedded-structs.md:
--------------------------------------------------------------------------------
1 | # Embedded Structs
2 |
3 | Go is not an [object-oriented](https://en.wikipedia.org/wiki/Object-oriented_programming) language. However, embedded structs provide a kind of *data-only* inheritance that can be useful at times. Keep in mind, Go doesn't support classes or inheritance in the complete sense, embedded structs are just a way to elevate and share fields between struct definitions.
4 |
5 | ```go
6 | type car struct {
7 | make string
8 | model string
9 | }
10 |
11 | type truck struct {
12 | // "car" is embedded, so the definition of a "truck" now also additionally contains all of the fields of the car struct
13 | car
14 | bedSize int
15 | }
16 | ```
17 |
18 | ## Embedded vs Nested Struct
19 |
20 | - An embedded struct's fields are accessed at the top level, unlike nested structs.
21 | - Promoted fields can be accessed like normal fields except that they can't be used in [composite literals](https://golang.org/ref/spec#Composite_literals)
22 |
23 | ```go
24 | lanesTruck := truck{
25 | bedSize: 10,
26 | car: car{
27 | make: "toyota",
28 | model: "camry",
29 | },
30 | }
31 |
32 | fmt.Println(lanesTruck.bedSize)
33 |
34 | // embedded fields promoted to the top-level
35 | // instead of lanesTruck.car.make
36 | fmt.Println(lanesTruck.make)
37 | fmt.Println(lanesTruck.model)
38 | ```
39 |
--------------------------------------------------------------------------------
/src/data/structs/nested-structs.md:
--------------------------------------------------------------------------------
1 | # Nested structs in Go
2 |
3 | Structs can be nested to represent more complex entities:
4 |
5 | ```go
6 | type car struct {
7 | Make string
8 | Model string
9 | Height int
10 | Width int
11 | FrontWheel Wheel
12 | BackWheel Wheel
13 | }
14 |
15 | type Wheel struct {
16 | Radius int
17 | Material string
18 | }
19 | ```
20 |
21 | The fields of a struct can be accessed using the dot `.` operator.
22 |
23 | ```go
24 | myCar := car{}
25 | myCar.FrontWheel.Radius = 5
26 | ```
27 |
--------------------------------------------------------------------------------
/src/data/structs/struct-methods.md:
--------------------------------------------------------------------------------
1 | # Struct Methods in Go
2 |
3 | While Go is **not** object-oriented, it does support methods that can be defined on structs. Methods are just functions that have a receiver. A receiver is a special parameter that syntactically goes *before* the name of the function.
4 |
5 | ```go
6 | type rect struct {
7 | width int
8 | height int
9 | }
10 |
11 | // area has a receiver of (r rect)
12 | func (r rect) area() int {
13 | return r.width * r.height
14 | }
15 |
16 | r := rect{
17 | width: 5,
18 | height: 10,
19 | }
20 |
21 | fmt.Println(r.area())
22 | // prints 50
23 | ```
24 |
25 | A receiver is just a special kind of function parameter. Receivers are important because they will allow us to define interfaces that our structs (and other types) can implement.
26 |
--------------------------------------------------------------------------------
/src/data/structs/structs-in-go.md:
--------------------------------------------------------------------------------
1 | # Structs in Go
2 |
3 | We use `structs` in Go to represent structured data. A struct type is defined with the keyword type, the name of the struct type, the keyword struct, and a pair of braces ({}). Within the braces, you list the fields in the struct. Just as you put the variable name first and the variable type second in a var declaration, you put the struct field name first and the struct field type second. It's often convenient to group different types of variables together. For example, if we want to represent a car we could do the following:
4 |
5 | ```go
6 | type car struct {
7 | Make string
8 | Model string
9 | Height int
10 | Width int
11 | }
12 | ```
13 |
14 | This creates a new struct type called `car`. All cars have a `Make`, `Model`, `Height` and `Width`. In Go, you will often use a `struct` to represent information that you would have used a dictionary for in Python, or an object literal for in JavaScript. You can define a struct type inside or outside of a function. A struct type that’s defined within a function can be used only within that function.
15 |
16 | > If you already know an object-oriented language, you might be wondering about the difference between classes and structs. The difference is simple: Go doesn’t have classes, because it doesn’t have inheritance. This doesn’t mean that Go doesn’t have some of the features of object-oriented languages it just does things a little differently.
17 |
--------------------------------------------------------------------------------
/src/data/variables-and-types/basic-types.md:
--------------------------------------------------------------------------------
1 | # Basic Types
2 |
3 | Go's basic variable types are:
4 |
5 | ```go
6 | bool
7 |
8 | string
9 |
10 | int int8 int16 int32 int64
11 | uint uint8 uint16 uint32 uint64 uintptr
12 |
13 | byte // alias for uint8
14 |
15 | rune // alias for int32
16 | // represents a Unicode code point
17 |
18 | float32 float64
19 |
20 | complex64 complex128
21 | ```
22 |
23 | `String`s and `int`s should be fairly self-explanatory. A `bool` is a boolean variable, meaning it has a value of `true` or `false`. The [floating point](https://en.wikipedia.org/wiki/Floating-point_arithmetic) types (`float32` and `float64`) are used for numbers that are not integers -- that is, they have digits to the right of the decimal place, such as `3.14159`. The `float32` type uses 32 bits of precision, while the `float64` type uses 64 bits to be able to more precisely store more digits. Don't worry too much about the intricacies of the other types for now.
24 |
25 | ## Declaring a Variable
26 |
27 | Variables are declared using the `var` keyword. For example, to declare a variable called `number` of type `int`, you would write:
28 |
29 | ```go
30 | var number int
31 | ```
32 |
33 | To declare a variable called `pi` to be of type `float64` with a value of `3.14159`, you would write:
34 |
35 | ```go
36 | var pi float64 = 3.14159
37 | ```
38 |
39 | The value of an initialized variable with no assignment will be its [zero value](https://tour.golang.org/basics/12).
40 |
41 | ## Short Variable Declaration
42 |
43 | Inside a function (even the main function), the `:=` short assignment statement can be used in place of a `var` declaration.
44 |
45 | ```go
46 | var empty string
47 | ```
48 |
49 | Is the same as
50 |
51 | ```go
52 | empty := ""
53 | ```
54 |
55 | The `:=` operator infers the type of the new variable based on the value.
56 |
57 | ```go
58 | numCars := 10 // inferred to be an integer
59 |
60 | temperature := 0.0 // temperature is inferred to be a floating point value because it has a decimal point
61 |
62 | var isFunny = true // isFunny is inferred to be a boolean
63 | ```
64 |
65 | Outside of a function (in the [global/package scope](https://dave.cheney.net/2017/06/11/go-without-package-scoped-variables)), every statement begins with a keyword (`var`, `func`, and so on) and so the `:=` construct is not available.
--------------------------------------------------------------------------------
/src/data/variables-and-types/conditionals.md:
--------------------------------------------------------------------------------
1 | # Conditionals
2 |
3 | `if` statements in Go don't use parentheses around the condition:
4 |
5 | ```go
6 | if height > 4 {
7 | fmt.Println("You are tall enough!")
8 | }
9 | ```
10 |
11 | `else if` and `else` are supported as you would expect:
12 |
13 | ```go
14 | if height > 6 {
15 | fmt.Println("You are super tall!")
16 | } else if height > 4 {
17 | fmt.Println("You are tall enough!")
18 | } else {
19 | fmt.Println("You are not tall enough!")
20 | }
21 | ```
22 |
23 | Here are some of the comparison operators in Go:
24 |
25 | ```go
26 | - `==` // equal to
27 | - `!=` // not equal to
28 | - `<` // less than
29 | - `>` // greater than
30 | - `<=` // less than or equal to
31 | - `>=` // greater than or equal to
32 | ```
33 |
34 | ## The Initial Statement of an if Block
35 |
36 | An `if` conditional can have an "initial" statement. The variable(s) created in the initial statement are *only* defined within the scope of the `if` body.
37 |
38 | ```go
39 | if INITIAL_STATEMENT; CONDITION {
40 | }
41 | ```
42 |
43 | ### Why would I use this?
44 |
45 | This is just some syntactic sugar that Go offers to shorten up code in some cases. For example, instead of writing:
46 |
47 | ```go
48 | length := getLength(email)
49 | if length < 1 {
50 | fmt.Println("Email is invalid")
51 | }
52 | ```
53 |
54 | We can do:
55 |
56 | ```go
57 | if length := getLength(email); length < 1 {
58 | fmt.Println("Email is invalid")
59 | }
60 | ```
61 |
62 | Not only is this code a bit shorter, but it also removes `length` from the parent scope, which is convenient because we don't need it there - we only need access to it while checking a condition.
63 |
--------------------------------------------------------------------------------
/src/data/variables-and-types/constants.md:
--------------------------------------------------------------------------------
1 | # Constants
2 |
3 | Constants are declared like variables but use the `const` keyword. Constants can't use the `:=` short declaration syntax. Constants can be character, string, boolean, or numeric values. They *can not* be more complex types like slices, maps and structs, which are types we will explain later. As the name implies, the value of a constant can't be changed after it has been declared.
4 |
5 | ## Computed Constants
6 |
7 | Constants must be known at compile time. More often than not they will be declared with a static value:
8 |
9 | ```go
10 | const myInt = 15
11 | ```
12 |
13 | However, constants *can be computed* so long as the computation can happen at *compile time*. For example, this is valid:
14 |
15 | ```go
16 | const firstName = "John"
17 | const lastName = "Doe"
18 | const fullName = firstName + " " + lastName // John Doe
19 | ```
20 |
21 | That said, you *cannot* declare a constant that can only be computed at run-time.
22 |
--------------------------------------------------------------------------------
/src/data/variables-and-types/formatting-strings.md:
--------------------------------------------------------------------------------
1 | # Formatting Strings in Go
2 |
3 | Go follows the [printf tradition](https://cplusplus.com/reference/cstdio/printf/) from the C language. Honestly speaking, string formatting/interpolation in Go is currently *less* elegant than JavaScript and Python
4 |
5 | - [fmt.Printf](https://pkg.go.dev/fmt#Printf) - Prints a formatted string to [standard output](https://stackoverflow.com/questions/3385201/confused-about-stdin-stdout-and-stderr).
6 | - [fmt.Sprintf()](https://pkg.go.dev/fmt#Sprintf) - Returns the formatted string
7 |
8 | ## Examples
9 |
10 | ### %v - Interpolate the default representation
11 |
12 | The `%v` variant prints the Go syntax representation of a value. You can usually use this if you're unsure what else to use. That said, it's better to use the type-specific variant if you can.
13 |
14 | ```go
15 | fmt.Printf("I am %v years old", 10)
16 | // I am 10 years old
17 |
18 | fmt.Printf("I am %v years old", "way too many")
19 | // I am way too many years old
20 | ```
21 |
22 | ### `%s` - Interpolate a string
23 |
24 | ```go
25 | fmt.Printf("I am %s years old", "way too many")
26 | // I am way too many years old
27 | ```
28 |
29 | ### `%d` - Interpolate an integer in decimal form
30 |
31 | ```go
32 | fmt.Printf("I am %d years old", 10)
33 | // I am 10 years old
34 | ```
35 |
36 | ### `%f` - Interpolate a decimal
37 |
38 | ```go
39 | fmt.Printf("I am %f years old", 10.523)
40 | // I am 10.523000 years old
41 |
42 | // The ".2" rounds the number to 2 decimal places
43 | fmt.Printf("I am %.2f years old", 10.523)
44 | // I am 10.53 years old
45 | ```
46 |
47 | If you're interested in all the formatting options, feel free to take a look at the `fmt` package's [docs here](https://pkg.go.dev/fmt#hdr-Printing).
--------------------------------------------------------------------------------
/src/data/variables-and-types/type-inference.md:
--------------------------------------------------------------------------------
1 | # Type Inference
2 |
3 | To declare a variable without specifying an explicit type (either by using the `:=` syntax or `var = expression` syntax), the variable's type is *inferred* from the value on the right hand side. When the right hand side of the declaration is typed, the new variable is of that same type:
4 |
5 | ```go
6 | var i int
7 | j := i // j is also an int
8 | ```
9 |
10 | However, when the right hand side is a literal value (an untyped numeric constant like `42` or `3.14`), the new variable will be an `int`, `float64`, or `complex128` depending on its precision:
11 |
12 | ```go
13 | i := 42 // int
14 | f := 3.14 // float64
15 | g := 0.867 + 0.5i // complex128
16 | ```
17 |
18 | ## Same Line Declarations
19 |
20 | We are able to declare multiple variables on the same line:
21 |
22 | ```go
23 | mileage, company := 80276, "Tesla"
24 |
25 | // is the same as
26 |
27 | mileage := 80276
28 | company := "Tesla"
29 | ```
30 |
--------------------------------------------------------------------------------
/src/data/variables-and-types/type-sizes.md:
--------------------------------------------------------------------------------
1 | # Type Sizes
2 |
3 | Ints, [uints](https://www.cs.utah.edu/~germain/PPS/Topics/unsigned_integer.html#:~:text=Unsigned%20Integers,negative%20(zero%20or%20positive).), [floats](https://techterms.com/definition/floatingpoint), and [complex](https://www.cloudhadoop.com/2018/12/golang-tutorials-complex-types-numbers.html#:~:text=Golang%20Complex%20Type%20Numbers,complex%20number%20is%2012.8i.) numbers all have type sizes.
4 |
5 | ```go
6 | int int8 int16 int32 int64 // whole numbers
7 |
8 | uint uint8 uint16 uint32 uint64 uintptr // positive whole numbers
9 |
10 | float32 float64 // decimal numbers
11 |
12 | complex64 complex128 // imaginary numbers (rare)
13 | ```
14 |
15 | The size (8, 16, 32, 64, 128, etc) indicates how many bits in memory will be used to store the variable. The default `int` and `uint` types are just aliases that refer to their respective 32 or 64 bit sizes depending on the environment of the user. The standard sizes that should be used unless the developer has a specific need are:
16 |
17 | - `int`
18 | - `uint`
19 | - `float64`
20 | - `complex128`
21 |
22 | Some types can be converted the following way:
23 |
24 | ```go
25 | temperatureInt := 88
26 | temperatureFloat := float64(temperatureInt)
27 | ```
28 |
29 | Casting a float to an integer in this way [truncates](https://techterms.com/definition/truncate) the floating point portion.
30 |
31 | ## Which Type Should I Use?
32 |
33 | With so many types for what is essentially just a number, developers coming from languages that only have one kind of `Number` type (like JavaScript) may find the choices daunting.
34 |
35 | ### Prefer "default" types
36 |
37 | A problem arises when we have a `uint16`, and the function we are trying to pass it into takes an `int`. We're forced to write code riddled with type casts like `int(myUint16)`. This style of development can be slow and annoying to read. When Go developers stray from the “default” type for any given type family, the code can get messy quickly. Unless you have a good reason to, stick to the following types:
38 |
39 | - `bool`
40 | - `string`
41 | - `int`
42 | - `uint32`
43 | - `byte`
44 | - `rune`
45 | - `float64`
46 | - `complex128`
47 |
48 | ### When should I use a more specific type?
49 |
50 | When you're super concerned about performance and memory usage. That’s about it. The only reason to deviate from the defaults is to squeeze out every last bit of performance when you are writing an application that is resource-constrained. (Or, in the special case of `uint64`, you need an absurd range of unsigned integers).
51 |
--------------------------------------------------------------------------------
/src/data/variables-and-types/wrapping-up.md:
--------------------------------------------------------------------------------
1 | # Wrapping Up
2 |
3 | ## Building Blocks of Data: Variables and Types in Go
4 |
5 | In the world of programming, data is king. Go provides a set of powerful building blocks for representing and manipulating data, known as variables and types. Let's explore how to create and use these crucial elements:
6 |
7 | ### Fundamental Types: The Foundation of Data
8 |
9 | Go offers a variety of basic data types to represent different kinds of information. Here are some of the most commonly used ones:
10 |
11 | - `int`: Integer numbers (e.g., 10, -5, 0).
12 | - `float64`: Floating-point numbers (e.g., 3.14, 2.718).
13 | - `string`: Textual data (e.g., "Hello", "World!").
14 | - `bool`: Boolean values (true or false).
15 |
16 | ### Declaring Variables: Giving Data a Name
17 |
18 | In Go, you declare variables using the `var` keyword. Let's see how it works:
19 |
20 | ```go
21 | var name string = "Alice" // Declare a string variable named "name" and assign the value "Alice"
22 | var age int = 30 // Declare an integer variable named "age" and assign the value 30
23 | ```
24 |
25 | ### Type Inference: Go's Smart Shortcut
26 |
27 | Go often infers the type of a variable based on the value assigned to it. This is known as type inference:
28 |
29 | ```go
30 | name := "Bob" // The type of "name" is inferred to be "string"
31 | age := 25 // The type of "age" is inferred to be "int"
32 | ```
33 |
34 | ### Constants: Unchanging Values
35 |
36 | Sometimes you need values that stay the same throughout your program. Constants are declared using the `const` keyword:
37 |
38 | ```go
39 | const PI float64 = 3.14159 // Define a constant named "PI" with the value 3.14159
40 | ```
41 |
42 | ### Type Sizes: Understanding Memory Usage
43 |
44 | Go provides information about the size of its data types. This helps optimize memory usage and understand how much space your data will occupy. You can check type sizes using the `unsafe` package, but generally, it's best to rely on the default type sizes provided by Go.
45 |
46 | ### Making Decisions: Conditionals and `if` Statements
47 |
48 | You can control the flow of your program using conditional statements (like `if` statements):
49 |
50 | ```go
51 | if age >= 18 {
52 | fmt.Println("You are an adult.")
53 | } else {
54 | fmt.Println("You are not an adult yet.")
55 | }
56 | ```
57 |
58 | ### Formatting Strings: Making Your Output Elegant
59 |
60 | Go provides a powerful formatting mechanism for strings:
61 |
62 | ```go
63 | name := "Alice"
64 | age := 30
65 | message := fmt.Sprintf("My name is %s, and I am %d years old.", name, age)
66 | fmt.Println(message) // Output: "My name is Alice, and I am 30 years old."
67 | ```
68 |
69 | Now you have a grasp of the fundamental building blocks of Go's data handling system! In the next steps, you'll explore how to work with functions, data structures, and more. With this foundation, you're well on your way to building sophisticated Go programs. Happy coding!
70 |
--------------------------------------------------------------------------------
/src/hooks/useKey.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 |
3 | export function useKey(key: string, action: () => void) {
4 | useEffect(
5 | function () {
6 | function callback(e: KeyboardEvent) {
7 | if (e.code.toLowerCase() === key.toLowerCase()) {
8 | action();
9 | }
10 | }
11 |
12 | document.addEventListener('keydown', callback);
13 |
14 | return function () {
15 | document.removeEventListener('keydown', callback);
16 | };
17 | },
18 | [action, key]
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import { HelmetProvider } from 'react-helmet-async';
4 | import App from './App.tsx';
5 | import './index.css';
6 |
7 | ReactDOM.createRoot(document.getElementById('root')!).render(
8 |
9 |
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/src/utils/capitalizedWord.ts:
--------------------------------------------------------------------------------
1 | // removes unnecessary hyphens and capitalizes words
2 | export function capitalizeWords(str: string) {
3 | return str
4 | ?.replaceAll('-', ' ')
5 | .split(' ')
6 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
7 | .join(' ');
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/checkRoute.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { categories } from './lists';
3 | import { useNavigate, useParams } from 'react-router-dom';
4 |
5 | function checkRoute(exercises?: string[]) {
6 | const { category } = useParams();
7 | const navigate = useNavigate();
8 |
9 | useEffect(() => {
10 | // if user navigated to a topic page
11 | if (category) {
12 | // check if category exists defined by us
13 | const isCategoryExist = categories.some(
14 | (prevCategory) =>
15 | prevCategory.link.slice(1).toLowerCase() === category?.toLowerCase()
16 | );
17 |
18 | // navigate to error page if user entered invalid category (the check is necessary because they might enter a URL manually and be invalid)
19 | if (!isCategoryExist) {
20 | console.log('Category does not exist:', category);
21 | navigate('/error');
22 | }
23 |
24 | return;
25 | }
26 |
27 | // if single exercise page isn't one of the three type, navigate to error page
28 | if (
29 | !exercises?.some(
30 | (exercise) =>
31 | exercise === 'beginner' ||
32 | exercise === 'intermediate' ||
33 | exercise === 'advanced'
34 | )
35 | ) {
36 | navigate('/error');
37 | }
38 | }, [category, exercises, navigate]);
39 | }
40 |
41 | export default checkRoute;
42 |
--------------------------------------------------------------------------------
/src/utils/cn.ts:
--------------------------------------------------------------------------------
1 | import { ClassValue, clsx } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | declare module '*.md';
3 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
4 | darkMode: 'class',
5 | theme: {
6 | extend: {
7 | fontFamily: {
8 | InterMedium: ['InterMedium', 'sans-serif'],
9 | InterBold: ['InterBold', 'sans-serif'],
10 | },
11 | },
12 | },
13 | plugins: [],
14 | };
15 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2021", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true
25 | },
26 | "include": ["src"]
27 | }
28 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [
7 | react(),
8 | // Custom plugin to load markdown files
9 | {
10 | name: 'markdown-loader',
11 | transform(code, id) {
12 | if (id.slice(-3) === '.md') {
13 | // For .md files, get the raw content
14 | return `export default ${JSON.stringify(code)};`;
15 | }
16 | },
17 | },
18 | ],
19 | });
20 |
--------------------------------------------------------------------------------