1 · Values and control flow · 25 MIN
Loops, slices and shared storage
A slice describes a view of an underlying array.
range visits slice elements in index order. This example creates a separate result slice before appending matches, leaving the input unchanged. A slice assignment alone would share backing storage; changing an element through either view can affect the other. append may allocate a new array when capacity is exhausted, which is why code should use its returned slice. nil and empty slices both have length zero, but JSON representations can differ. State whether your API promises an empty array or null.
make([]int, 0) gives the result its own append lifecycle.
Read the example
package main
import "fmt"
func passing(scores []int) []int {
result := make([]int, 0)
for _, score := range scores { if score >= 60 { result = append(result, score) } }
return result
}
func main() { scores := []int{59,60,90}; fmt.Println(passing(scores)); fmt.Println(scores) }Check the expected output
[60 90] [59 60 90]
Your challenge
Extend passing to accept a minimum score parameter while preserving order and the input.
Solution cost: O(n) comparisons with up to O(n) result storage. time · Account for collection storage separately from the returned result. space
Common trap
Reusing scores[:0] as the result modifies the original backing array.
Study the project implementation
package main
import "fmt"
func passing(scores []int) []int {
result := make([]int, 0)
for _, score := range scores { if score >= 60 { result = append(result, score) } }
return result
}
func main() { scores := []int{59,60,90}; fmt.Println(passing(scores)); fmt.Println(scores) }Further reading: Official documentation
Next lesson: Structs, maps and missing values →