I was following the example https://tour.golang.org/moretypes/10
I modified the code expecting to get the same result. I did not. Is this a bug, or a documentation error? The tour states
A nil slice has a length and capacity of 0.
My y variable has a length and capacity of 0.
package main
import "fmt"
func myPrint(z []int) {
fmt.Println(z, len(z), cap(z))
if z == nil {
fmt.Println("nil!")
}
}
func main() {
var z []int
y := []int {}
myPrint(z)
myPrint(y)
}
Here is my output.
[] 0 0
nil!
[] 0 0
I was expecting a second "nil"~ Why didn't I get it?
nil Vs empty slice
If we think of a slice like this:
[pointer] [length] [capacity]
then:
nil slice: [nil][0][0]
empty slice: [addr][0][0] // it points to an address
From: "Go in action" book:
nil slice
They’re useful when you want to represent a slice that doesn’t exist, such as when an exception occurs in a function that returns a slice.
// Create a nil slice of integers.
var slice []int
empty slice
Empty slices are useful when you want to represent an empty collection, such as when a database query returns zero results.
// Use make to create an empty slice of integers.
slice := make([]int, 0)
// Use a slice literal to create an empty slice of integers.
slice := []int{}
👉 Regardless of whether you’re using a nil slice or an empty slice, the built-in functions append, len, and cap work the same.
Go playground example:
package main
import (
"fmt"
)
func main() {
var nil_slice []int
var empty_slice = []int{}
fmt.Println(nil_slice == nil, len(nil_slice), cap(nil_slice))
fmt.Println(empty_slice == nil, len(empty_slice), cap(empty_slice))
}
prints:
true 0 0
false 0 0
The doc you referenced states that a nil slice has a length and capacity of 0, but not that every slice of length and capacity of zero is a nil slice. The specification only says that the value of an uninitialized slice is nil.
This is a convenience to support len and cap on slices which are uninitialised (nil). Otherwise we would need to check for non-nil first in order to avoid panic. (This also holds for other in-built types like maps or channels.)
In terms of the fmt.Print output, the difference in behaviour is similar to printing an uninitialised (nil) pointer vs pointer to an empty structure:
var s *struct{} // uninitialised pointer
fmt.Println(s) // <nil>
s = &struct{}{} // pointer to an empty structure
fmt.Println(s) // &{}
In this case:
var z []int
You have declared a variable z but you did not initialize it.
In this case:
y := []int {}
You declared it and initialized it, you set it to an empty slice. Writing the second expression the long way makes the difference between the two expressions more clear:
var y []int = []int {}
Your y variable isn't the zero value for a slice. It's allocated via an empty slice literal.
// both of these allocate a slice
y := []int{}
z := []int{1, 2, 3}
A nil slice has a length and capacity of 0 and has no underlying array.
var s []string => no underlying array
var s = []string => create a underlying array but his length is 0.
Related
Suppose a function such as:
func returnNamedSlice(num int) (s []int)
I am able to do the following directly in the code, as if s was already made.
s = append(s, 5)
But if I don't do the above (append operation), then s is always nil and the returned s is also nil.
Why this design? This seems very inconsistent.
The named return slice is initialized to nil. The following statement works because a nil slice is handled the same as an empty slice by the append function.
s = append(s, 5)
Nil slices are handled the same as empty slices because the length and capacity of a nil slice are defined to be zero, the same as an empty slice.
The feature is unrelated to named return values. Here's a demonstration without return values:
var x []int // x is a nil slice of int
fmt.Println(x) // prints []
fmt.Println(x == nil) // prints true
x = append(x, 5) // x is slice with one element, 5
fmt.Println(x) // prints [5]
fmt.Println(x == nil) // prints false
A confusing point when examining these features is that the fmt package prints nil slices and empty slices with the same representation, [].
You can append items to nil slice too, no matter it's nil or not.
Look at the example below:
package main
import "log"
func main() {
a := a() // no matter it returns nil
log.Println("Is a nil: ", a == nil)
a = append(a, 1) // you can append items to nil slice
log.Println(a)
//
b := b()
b = append(b, 2)
log.Println(b)
}
func a() (s []int) {
return
}
func b() (s []int) {
s = append(s, 5)
return
}
And the result:
2022/06/19 09:52:02 Is a nil: true
2022/06/19 09:52:02 [1]
2022/06/19 09:52:02 [5 2]
Any named-return-variable ("result parameter", as the spec calls it) in Go is "pre-initialized" to the appropriate zero value, exactly the same as any locally-defined variable that has no initializer:
var i int
var f float64
var s string
i is zero (integer), f is zero (0.0, float64), and s is "zero" (empty string ""). So it is with your function:
func returnNamedSlice(num int) (s []int) {
// ... code here ...
return
}
At the top of the function, where the // code here code goes, s is initialized to "zero", i.e., []int(nil): nil as converted to []int. This is described under the Return statements section of the Go spec.
You must set s to some non-nil value to return some non-nil value. You do not have to use append but it's pretty standard to append to the appropriately typed nil since that makes it easy to build up a list in a loop or with a series of if tests or whatever:
if conditionA {
s = append(s, 42)
}
if conditionB {
s = append(s, 6, 9)
}
But:
if conditionC {
s = []int{3, 1, 4, 1, 5, 9}
}
will work too.
Whatever s is set to in the end, that's what it will hold at return time. Note that:
return someval
means (1) assign the value to s, and then (2) return (in that order). If a deferred function then calls panic and there's a panic handler that traps the panic and uses s and/or assigns a new value to s, this matters; see the Defer statement examples in the spec.
To get length of any slice, I use reflect.ValueOf(slice).Len().
To set length of any slice, I use reflect.ValueOf(&slice).Elem().SetLen(n).
I have a field of type reflect.Value in my struct, and the value is set to be reflect.ValueOf(&slice) so that I can change the slice. But now I can't get the length of the underlying slice.
It would panic because of call of reflect.Value.Len on ptr Value if I call Len() directly, and call of reflect.Value.Len on interface Value if I call Elem().Len().
Below is the function I was trying to implement:
func pop(slice interface{}) interface{} {
v := reflect.ValueOf(slice)
length := v.Len()
last := v.Index(length - 1)
v.SetLen(length - 1)
return last
}
How can I do both with refect.Value of the slice pointer?
Write the function to work with a pointer to slice argument.
// pop removes and returns the last element from
// the slice pointed to by slicep.
func pop(slicep interface{}) interface{} {
v := reflect.ValueOf(slicep).Elem()
length := v.Len()
last := v.Index(length - 1)
v.SetLen(length - 1)
return last
}
Call it like this:
slice := []int{1, 2, 3}
last := pop(&slice)
fmt.Println(last) // prints 3
fmt.Println(slice) // prints [1 2]
Run it on the playground
I am trying to assign a value to the empty slice as follows.
func main() {
var s []int
fmt.Println(s, len(s), cap(s))
s[0] = 99
}
And it throws an exception,
panic: runtime error: index out of range
Note:
I know one way of doing this by initializing the value at declaration part as follows. But in the above example I am trying to assign a value after the declaration.
var s []int{99}
Is there a way to achieve this?
Empty slices cannot just be assigned to. Your print statement shows that the slice has length and capacity of 0. Indexing at [0] is definitely out of bounds.
You have (at least) three choices:
Append to the slice: s = append(s, 99)
or Initialize the slice to be non-empty: s := make([]int, 1)
or Initialize your slice with the element you want: s := []int{99}
You can find tutorials on slices in the Go tour, or a lot more details about slice usage and internals.
var s []int{99}
The above works but if you want to assign after declaration, then you would need to create a slice using make function with enough length
s := make([]int, 10)
s[0] = 10
fmt.Println(s)
This will initialize slice and set the length to 10 and its elements to zero values
Note: doing s[10] or any greater index will panic since the slice is initialised with length 10. If you want to dynamically increase the slice size, then use append
You can do that by using append function.
func main() {
var s []int
s = append(s,99)
fmt.Println(s) // [99]
}
https://play.golang.org/p/XATvSo2OB6f
// slice declaration; no memory allocation
var slice []int
//slice initialization with length (0) and capacity (10);
//memory allocated for 10 ints
slice = make([]int, 0, 10)
// push to the slice value - than increase length
slice = append(slice, 1)
//change the value. Index has to be lower then length of slice
slice[0] = 2
Take a loot at this output - https://play.golang.com/p/U426b1I5zRq
Of course, you can skip initialization with make, append will do it for you with default value of capacity (2). But for performance it is better to allocate memory only once (if you know how many elements are going to be added to the slice)
In Go, what is the difference between var s []int and s := make([]int, 0)?
I find that both works, but which one is better?
Simple declaration
var s []int
does not allocate memory and s points to nil, while
s := make([]int, 0)
allocates memory and s points to memory to a slice with 0 elements.
Usually, the first one is more idiomatic if you don't know the exact size of your use case.
In addition to fabriziom's answer, you can see more examples at "Go Slices: usage and internals", where a use for []int is mentioned:
Since the zero value of a slice (nil) acts like a zero-length slice, you can declare a slice variable and then append to it in a loop:
// Filter returns a new slice holding only
// the elements of s that satisfy f()
func Filter(s []int, fn func(int) bool) []int {
var p []int // == nil
for _, v := range s {
if fn(v) {
p = append(p, v)
}
}
return p
}
It means that, to append to a slice, you don't have to allocate memory first: the nil slice p int[] is enough as a slice to add to.
Just found a difference. If you use
var list []MyObjects
and then you encode the output as JSON, you get null.
list := make([]MyObjects, 0)
results in [] as expected.
A bit more complete example (one more argument in .make()):
slice := make([]int, 2, 5)
fmt.Printf("length: %d - capacity %d - content: %d", len(slice), cap(slice), slice)
Out:
length: 2 - capacity 5 - content: [0 0]
Or with a dynamic type of slice:
slice := make([]interface{}, 2, 5)
fmt.Printf("length: %d - capacity %d - content: %d", len(slice), cap(slice), slice)
Out:
length: 2 - capacity 5 - content: [<nil> <nil>]
I'm trying to Insert a Int value to slice if it is missing in that.
My Code :
package main
import (
"fmt"
)
func AppendIfMissing(slice []int, i int) []int {
for _, ele := range slice {
if ele == i {
fmt.Println(i)
return slice
}
}
fmt.Println("i value is ", i)
slice = append(slice, i)
return slice
}
func main() {
slice1 := []int{1, 2, 3, 4}
AppendIfMissing(slice1, 60)
fmt.Println("slice after adding :", slice1)
}
OutPut :
i value is 60
slice after adding : [1 2 3 4]
Appending to slice is not happening.What is wrong with my code ?
AppendIfMissing returns a slice which you need to affect to a variable.
append(slice, i) creates a new slice, which means the parameter slice isn't modified, it refers to a all new slice:
which is returned at the end
which needs to be affected to a variable
slice1 = AppendIfMissing(slice1, 60)
See go playground example.
I agree that the article "Arrays, slices (and strings): The mechanics of 'append'" mentions
Even though the slice header is passed by value, the header includes a pointer to elements of an array, so both the original slice header and the copy of the header passed to the function describe the same array.
Therefore, when the function returns, the modified elements can be seen through the original slice variable.
But the function in that article wasn't using append:
func AddOneToEachElement(slice []byte) {
for i := range slice {
slice[i]++
}
}
the contents of a slice argument can be modified by a function, but its header cannot
And by doing
slice = append(slice, i)
you modify the header, you reallocate the resulting slice to a completely different array.
That won't be visible outside of the function.
More generic version of AppendIfMissing, it requires go version >= 1.18
func AppendIfMissing[T comparable](slice []T, i T) []T {
for _, ele := range slice {
if ele == i {
return slice
}
}
return append(slice, i)
}