package main
import (
"fmt"
)
type Person struct {
name string
}
func main() {
p := make([]*Person, 0)
p = append(p, &Person{"Brian"})
fmt.Println(p[0].name)
p = append(p, &Person{"Le Tu"})
fmt.Println(p[1].name)
}
The above works fine.
package main
import (
"fmt"
)
type Person struct {
name string
}
func main() {
p := make([]*Person, 1) //Changed to 1 instead of 0
p = append(p, &Person{"Brian"})
fmt.Println(p[0].name)
p = append(p, &Person{"Le Tu"})
fmt.Println(p[1].name)
}
The above panics.
My understanding of append
was that it hid the mechanics of extending/adding. Clearly, my mental model of using append
as a sort of "push" for slices is incorrect. Can anyone explain to me why the second sample above panics? Why can't I just append
my struct?
For example,
package main
import (
"fmt"
)
type Person struct {
name string
}
func main() {
p := make([]*Person, 1) //Changed to 1 instead of 0
fmt.Println(len(p), p)
p = append(p, &Person{"Brian"})
fmt.Println(len(p), p)
fmt.Println(p[1].name)
fmt.Println(p[0])
fmt.Println(p[0].name)
}
Output:
1 [<nil>]
2 [<nil> 0x10500190]
Brian
<nil>
panic: runtime error: invalid memory address or nil pointer dereference
p
has length 1 before the append, length 2 after. Therefore, p[0]
has the uninitialized pointer value nil
and p[0].name
is invalid.
The Go Programming Language Specification
Appending to and copying slices
The variadic function append appends zero or more values x to s of type S, which must be a slice type, and returns the resulting slice, also of type S.
Pointer types
The value of an uninitialized pointer is nil.
Selectors
The following rules apply to selectors:
4) If x is of pointer type and has the value nil and x.f denotes a struct field, assigning to or evaluating x.f causes a run-time panic.
Reference page for make
When using make
to build a slice, the first integer argument is the actual length of the created slice :
p := make([]*Person, 1)
// is equivalent to
p := []*Person{nil} //<- slice of length 1, cells contain the default
// zero value for the *Person type
If you want to create a slice of length 0, but with a predefined capacity, you need to use the 3 arguments version of make
:
p := make([]*Person, 0, 100) //<- slice of length 0, but the first 100 append
// won't reallocate the slice
A simple example on how to use both cases :
//100-cell slice :
p1 := make([]*Person, 100)
for i := 0; i < 100; i++ {
name := fmt.Sprintf("Foo %d", i+1)
//you can access and assign to p1[i] because p1 has 100 cells :
p1[i] = &Person{name}
}
fmt.Printf("p1[0].Name : %s\n", p1[0].Name)
fmt.Printf("p1[99].Name : %s\n", p1[99].Name)
//0-cell, 100-capacity slice :
p2 := make([]*Person, 0, 100)
for i := 0; i < 100; i++ {
name := fmt.Sprintf("Foo %d", i+1)
//you cannot access p2[i], the cell doesn't exist yet :
p2 = append(p2, &Person{name})
}
fmt.Printf("p2[0].Name : %s\n", p2[0].Name)
fmt.Printf("p2[99].Name : %s\n", p2[99].Name)
play.golang link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With