Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 13 additions & 34 deletions arrays-and-slices.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,49 +299,28 @@ This is valid, but our tests still won't compile!
`./sum_test.go:26:9: invalid operation: got != want (slice can only be compared to nil)`

Go does not let you use equality operators with slices. You _could_ write
a function to iterate over each `got` and `want` slice and check their values
but for convenience sake, we can use [`reflect.DeepEqual`][deepEqual] which is
useful for seeing if _any_ two variables are the same.
a function to iterate over each `got` and `want` slice and check their values,
but what if we had a more convenient way to do this?

```go
func TestSumAll(t *testing.T) {
From Go 1.21, [slices](https://pkg.go.dev/slices#pkg-overview) standard package is available, which has [slices.Equal](https://pkg.go.dev/slices#Equal) function to do a simple shallow compare on slices, where you don't need to worry about the types like the above case.
Note that this function expects the elements to be [comparable](https://pkg.go.dev/builtin#comparable).
So, it can't be applied to slices with non-comparable elements like 2D slices.

got := SumAll([]int{1, 2}, []int{0, 9})
want := []int{3, 9}

if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
```

\(make sure you `import reflect` in the top of your file to have access to `DeepEqual`\)

It's important to note that `reflect.DeepEqual` is not "type safe" - the code
will compile even if you did something a bit silly. To see this in action,
temporarily change the test to:
Let's go ahead and put this into practice!

```go
func TestSumAll(t *testing.T) {

got := SumAll([]int{1, 2}, []int{0, 9})
want := "bob"
want := []int{3, 9}

if !reflect.DeepEqual(got, want) {
if !slices.Equal(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
```

What we have done here is try to compare a `slice` with a `string`. This makes
no sense, but the test compiles! So while using `reflect.DeepEqual` is
a convenient way of comparing slices \(and other things\) you must be careful
when using it.

(From Go 1.21, [slices](https://pkg.go.dev/slices#pkg-overview) standard package is available, which has [slices.Equal](https://pkg.go.dev/slices#Equal) function to do a simple shallow compare on slices, where you don't need to worry about the types like the above case. Note that this function expects the elements to be [comparable](https://pkg.go.dev/builtin#comparable). So, it can't be applied to slices with non-comparable elements like 2D slices.)

Change the test back again and run it. You should have test output like the following

You should have test output like the following:
`sum_test.go:30: got [] want [3 9]`

## Write enough code to make it pass
Expand Down Expand Up @@ -482,9 +461,9 @@ panic: runtime error: slice bounds out of range [recovered]
panic: runtime error: slice bounds out of range
```

Oh no! It's important to note that while the test _has compiled_, it _has a runtime error_.
Oh no! It's important to note that while the test _has compiled_, it _has a runtime error_.

Compile time errors are our friend because they help us write software that works,
Compile time errors are our friend because they help us write software that works,
runtime errors are our enemies because they affect our users.

## Write enough code to make it pass
Expand Down Expand Up @@ -534,9 +513,9 @@ func TestSumAllTails(t *testing.T) {
}
```

We could've created a new function `checkSums` like we normally do, but in this case, we're showing a new technique, assigning a function to a variable. It might look strange but, it's no different to assigning a variable to a `string`, or an `int`, functions in effect are values too.
We could've created a new function `checkSums` like we normally do, but in this case, we're showing a new technique, assigning a function to a variable. It might look strange but, it's no different to assigning a variable to a `string`, or an `int`, functions in effect are values too.

It's not shown here, but this technique can be useful when you want to bind a function to other local variables in "scope" (e.g between some `{}`). It also allows you to reduce the surface area of your API.
It's not shown here, but this technique can be useful when you want to bind a function to other local variables in "scope" (e.g between some `{}`). It also allows you to reduce the surface area of your API.

By defining this function inside the test, it cannot be used by other functions in this package. Hiding variables and functions that don't need to be exported is an important design consideration.

Expand Down
5 changes: 2 additions & 3 deletions arrays/v4/sum_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"reflect"
"slices"
"testing"
)

Expand All @@ -18,15 +18,14 @@ func TestSum(t *testing.T) {
t.Errorf("got %d want %d given, %v", got, want, numbers)
}
})

}

func TestSumAll(t *testing.T) {

got := SumAll([]int{1, 2}, []int{0, 9})
want := []int{3, 9}

if !reflect.DeepEqual(got, want) {
if !slices.Equal(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
4 changes: 2 additions & 2 deletions arrays/v5/sum_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"reflect"
"slices"
"testing"
)

Expand All @@ -26,7 +26,7 @@ func TestSumAll(t *testing.T) {
got := SumAll([]int{1, 2}, []int{0, 9})
want := []int{3, 9}

if !reflect.DeepEqual(got, want) {
if !slices.Equal(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
4 changes: 2 additions & 2 deletions arrays/v6/sum_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"reflect"
"slices"
"testing"
)

Expand All @@ -26,7 +26,7 @@ func TestSumAllTails(t *testing.T) {
got := SumAllTails([]int{1, 2}, []int{0, 9})
want := []int{2, 9}

if !reflect.DeepEqual(got, want) {
if !slices.Equal(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
4 changes: 2 additions & 2 deletions arrays/v7/sum_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"reflect"
"slices"
"testing"
)

Expand All @@ -24,7 +24,7 @@ func TestSum(t *testing.T) {
func TestSumAllTails(t *testing.T) {

checkSums := func(t *testing.T, got, want []int) {
if !reflect.DeepEqual(got, want) {
if !slices.Equal(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
Expand Down
4 changes: 2 additions & 2 deletions arrays/v8/sum_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"reflect"
"slices"
"strings"
"testing"
)
Expand All @@ -25,7 +25,7 @@ func TestSum(t *testing.T) {
func TestSumAllTails(t *testing.T) {

checkSums := func(t *testing.T, got, want []int) {
if !reflect.DeepEqual(got, want) {
if !slices.Equal(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
Expand Down