I need your help with a question, go docs say:
"The assignment proceeds in two phases. First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order. Second, the assignments are carried out in left-to-right order." (Assignment statements)
From the text above I can assume that pointers and index expressions should be carried out in the standard order together, but it looks like Go carry out first indexes, then pointers, then everything else.
x := []int{1}
var a *[]int
a = &x
x[0], *a, x[0] = 1, []int{1, 2}, (*a)[1]
//result: index out of range [1] with length 1 (*a)[1]
however, I expected then *a will have a new slice capacity of 2, but it is not.
another example is to test the order of pointers and slices:
x[0], *a, x[0] = 1, []int{1, 2}, 999 //result: [1,2]
I expected during the left-right order, *a and x should have a new slice, and the expected result is [999,2].
To be more sure we can modify the previous example to:
*a, x[0] = nil, 666 //result: [] - but not a panic
It looks like Go has Three phases
Carry out all indexes
Carry out all pointers
Carry out everything else
Am I understanding it right, what is the real order of pointers and slices?
Thanks in advance!
what is the real order of pointers and slices?
Left to right, just as the docs say.
You've quoted the right section of the spec to answer your question, but it seems you misunderstand the language used. Read it plainly:
First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order. Second, the assignments are carried out in left-to-right order.
Now look at the first example:
x := []int{1}
var a *[]int
a = &x
x[0], *a, x[0] = 1, []int{1, 2}, (*a)[1]
When (*a)[1] is evaluated, none of the assignments on that line are carried out yet. Hence, the words "First" and "Second" in the quoted section. So, it tries to index []int{1}[1], which is invalid.
For the second example, all you must understand is that the expression x[0] corresponds to the 0 slot of slice x when the expression is evaluated. It doesn't matter if x gets reassigned after x[0] is evaluated, the already evaluated x[0] will still correspond to the 0 slot of the original slice.
The third example uses the same knowledge as the second.
The subtlety you may have not understood before is that index expressions and pointer indirections do not yield values, they yield variables. Slice/array elements are also considered to be variables for this purpose, so you can imagine a slice's underlying data as a series of distinct variables stored back-to-back. Thus, an index expression of x[0] resolves to some specific variable in memory that no longer depends on the value of x whatsoever. Remember, x is not a slice per se. x is just a variable that can denote some slice, or no slice at all, and that can change throughout the lifetime of x.
Related
I'm working my way through this "Tour of Go" and I'm doing this exercise here. I originally wrote the answer like this:
func Pic(dx, dy int) [][]uint8 {
outer := make([][]uint8, dy)
inner := make([]uint8, dx)
for y, _ := range outer {
for x, _ := range inner {
inner[x] = x^y // this is one of the image functions they give
}
outer[y] = inner
}
return outer
}
I thought this should work, but when I run Pic(5,5) I get:
[[0 4 8 12 16] [0 4 8 12 16] [0 4 8 12 16] [0 4 8 12 16] [0 4 8 12 16]]
Not right, obviously.
So, I finally figured out that I needed to move my inner := make([]uint8, dx) declaration inside of the first loop like so:
func Pic(dx, dy int) [][]uint8 {
outer := make([][]uint8, dy)
for y, _ := range outer {
inner := make([]uint8, dx)
for x, _ := range inner {
inner[x] = x^y // this is one of the image functions they give
}
outer[y] = inner
}
return outer
}
and now Pic(5,5) gives me the correct answer:
[[0 0 0 0 0] [0 1 2 3 4] [0 2 4 6 8] [0 3 6 9 12] [0 4 8 12 16]]
What's happening here? I think this has something to do with pointers or something, but I want to be sure I really understand what's going on. Obviously, in the first example, each time I reassigned something new to inner the values in outer that previously had inner assigned to them changed as well.
But what happens in the second example? Each time we go through the range outer loop the inner variable gets re-declared, but what happens to the values of outer that previously had inner assigned to them? Are they now somehow "cut off" and only exist as copies?
I'm mainly confused because the assignment line outer[y] = inner doesn't change in either example, so I don't understand the mechanics of how in one of them it assigns a copy of the value and in the other it assigns what I would think of as a pointer.
In the first case, you never "reassigned something new to inner". You created one slice of uint8, called inner, which you filled with values (overwriting those values several times), and you made every element of outer contain a copy of inner. But remember, a slice is only a view into an array that exists somewhere (in your case, the backing array is created anonymously by make). Copying a slice doesn't copy the values in the array, it just copies the slice header, meaning there are now multiple views onto the same storage (this is fine, and safe).
In the second case, you called make for every row of outer, so five different backing arrays were created, with inner pointing into a different one each time. This time, when you assigned inner to an element of outer, you got five genuinely different slice headers, instead of five copies of the same one.
In short: a slice is a value, but it's a value that contains a pointer. The pointer might point to an array that has a name, or it might point to an entirely anonymous array. There's nothing to worry about; Go will make sure that anything that is pointed-to stays alive.
First, consider this:
inner := make([]uint8, dx)
This statement allocates an array large enough to hold dx elements, creates a slice from it, and stores that slice in the variable inner. A slice contains three values: a pointer to the underlying array, capacity of that array, and the length of the slice. So inner contains a pointer to the actual data.
When you redeclare and assign inner in the for loop, a new array is created to store the data, inner becomes a slice pointing to that array, and then you fill out the individual elements of inner. Then you assign inner to an element of outer, and that element of outer now gets that slice, pointing to the initialized data. In the next iteration, a new array is created, inner is assigned to it, etc.
So it's got to do with pointers, in particular, the fact that a slice contains a pointer to the underlying array, and that when you assign a variable to a slice, that variable is assigned three values, the pointer to array, length, and capacity,
I need to populate a struct that has a member of type [8]uint8. This needs be populated with a byte array of type []byte initialized to length 8. The simplistic approach does not work:
Data: [8]uint8(RequestFrame(0x180, r)),
gives
cannot convert .. (type []byte) to type [8]uint8
Since both arrays are structurally identical it would be nice if this could be done with casting/assignment rather than copying?
Background
The problem with your "simplistic approach" is that a slice
(of any type) is a struct-typed value consisting of a pointer
and two integers; the pointer contains the address of the
underlying (backing) data array, and the integers contain
what len() and cap() builtins return for that slice.
In other words, a slice is sort of a view into an array.
Then, in Go, there is no concept of a type cast; there are only
type conversions, and these conversions may only happen between
types with the same underlying representation¹.
Since a slice and an array may not have the same underlying
representation (array is literally a contiguous block of memory
of the size just enough to contain all the array's elements),
your alleged type conversion may not be legal.
Possible solutions
There are two possible solutions.
The simplest is to just copy the data from the slice's
backing array into a newly-allocated array:
var (
src = []byte{1, 2, 3, 4, 5, 6, 7, 8}
dst [8]uint8
)
copy(dst[:], src[:8])
Note that there exists an inherent disparity between slice an
array types: an array type encodes both the type of its elements
and its length (that is, the length is a part of the type),
while a slice type only encodes the type of its elements
(and may be of any length at runtime).
This means that you might need to have a check before such
copying that makes sure the source slice has exactly 8
elements, that is, len(src) == len(dst).
This invariant may be enforced by some other code, but I think
I'd warn you up front about this: if src has less than 8
elements, the src[:8] expression will panic at runtime,
and if it contains more, then there's the question of whether
copying just the first 8 of them is exactly what's needed.
The second approach (admittedly messier) is to just re-use
the underlying array of the slice:
import "unsafe"
var (
src = []byte{1, 2, 3, 4, 5, 6, 7, 8}
dstPtr *[8]uint8
)
if len(src) != len(*dstPtr) {
panic("boom")
}
dstPtr = (*[8]uint8)(unsafe.Pointer(&src[0]))
Here, we've just taken the address of the first element
contained in the slice's underlying array and peformed
a "dirty" two-phase type-conversion to make the obtained
pointer to be of type *[8]uint8—that is, "an address of
an array of 8 uint8s".
Note two caveats:
The resulting pointer now points to
the same memory block the original slice does.
It means it's now possible to mutate that memory both through the
slice and the pointer we obtained.
As soon as you'll decide to assign the array's data
to a variable of type [8]uint8 (and passing it as an argument
to a function's parameter of that type), you will dereference
that pointer (like with *dstPtr), and at that moment
the array's data will be copied.
I'm specifically mentioning this as often people resort
to hacks like this one to pull the backing array out of
a slice precisely in an attempt to not copy the memory.
TL;DR
Copy the data (after supposedly verifying the
len(src) == len(dst) invariant holds).
Copying 8 bytes is fast (on a typical 64-bit CPU this will be
a single MOV instruction, or two at most), and the code will
be straightforward.
Only resort to hacks from the second solution when you really
need to optimize on some critical hot path.
In that case, comment the solution extensively and watch for
not accidentally dereferencing your pointer.
¹ There are notable exceptions to this rule:
A []byte is type-convertible to string, and vice-versa.
A string is type-convertible to []rune, and vice-versa.
An int is type-convertible to string (but since Go 1.15 go vet gives a warning about it, and this feature may probably be prohibited in the future).
You can copy the contents of your byte slice into your uint8 array very simply by using copy, like this:
package main
import (
"fmt"
)
func main() {
slice := []byte{1, 2, 3, 4, 5, 6, 7, 8}
array := [8]uint8{}
copy(array[:], slice)
fmt.Println(array)
}
Outputs
[1 2 3 4 5 6 7 8]
Try it out on the playground.
But may I ask why you are using an array? It's usually better to just use slices, unless you have a really good reason.
Starting from Go 1.17 you are able to use type conversion directly, from a slice to an array pointer:
a := make([]byte, 8)
b := (*[8]uint8)(a) // b is pointer to [8]uint8
The you can just dereference to obtain a non-pointer [8]uint8 type.
a := make([]byte, 8)
b := *(*[8]uint8)(a) // b is [8]uint8
Notes:
unlike copy, the conversion approach does not incur in extra allocations (not yours, nor any possibly done by copy), because it simply yields a pointer to the existing backing array. Though dereferencing the array pointer will make a copy.
the conversion panics if the length of the array is greater than the slice's
a := make([]byte, 5)
b := (*[10]byte)(a) // panics
the pointer points to the slice's underlying array, therefore the same values will be visible by indexing either:
a := []byte{0xa1, 0xa2}
b := (*[2]uint8)(a)
fmt.Printf("%x\n", a[0]) // a1
b[0] = 0xff
fmt.Printf("%x\n", a[0]) // ff
you can convert from byte to uint8, including type literals derived from them, because byte is an alias (identical to) of uint8.
Related: How do you convert a slice into an array?
Can anyone explain to me what is this line doing? I've never seen this before, I guess.
np.Point, np.Valid = Point{}, false
As stated in this github code
This is not a three comma syntax. It is actually initializing two variables together in a line
np.Point, np.Valid = Point{}, false
is similar to
np.Point = Point{}
np.Valid = false
The Go Programming Language Specification
Assignments
A tuple assignment assigns the individual elements of a multi-valued
operation to a list of variables. There are two forms. In the first,
the right hand operand is a single multi-valued expression such as a
function call, a channel or map operation, or a type assertion. The
number of operands on the left hand side must match the number of
values. For instance, if f is a function returning two values,
x, y = f()
assigns the first value to x and the second to y. In the second form,
the number of operands on the left must equal the number of
expressions on the right, each of which must be single-valued, and the
nth expression on the right is assigned to the nth operand on the
left:
one, two, three = '一', '二', '三'
A tuple assignment assigns the individual elements of a multi-valued
operation to a list of variables. In the second form, the number of operands on the left must equal the number of > expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left.
In your example,
np.Point, np.Valid = Point{}, false
Or, equivalently,
t1 := Point{}
t2 := false
np.Point = t1
np.Valid = t2
Please check the below sample code, and look into 3rd line.
a := [3]int{10,20}
var i int = 50
i, a[2] = 100, i
fmt.Println(i) //100
fmt.Println(a) //[10 20 50]
I have overwritten the value 100 in i variable and immediately applied the int array. When I printed the array, the new value was not printed. How does multiple variable assignment work in Go? Why the i value is not updated into the array immediately?
The assigment section of the Go spec mentions:
The assignment proceeds in two phases.
First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order.
Second, the assignments are carried out in left-to-right order.
That means:
var i int = 50
i, a[2] = 100, i
a[2] is assigned the i evaluated before assignment (50)
i is assigned 100
This is intended and described in the Go language specs.
Basically it is one statement which happens to assign 2 values to 2 variables. The effects of the statement are available/visible when the statement is fully executed, like with any other expression.
The value of i changes the moment you "hit" line 4, so at the time of the assignment to a[3] its value is still 50.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
According to the Tour of Go, in a Go slice s, the expression s[lo:hi] evaluates to a slice of the elements from lo through hi-1, inclusive:
package main
import "fmt"
func main() {
p := []int{0, // slice position 0
10, // slice position 1
20, // slice position 2
30, // slice position 3
40, // slice position 4
50} // slice position 5
fmt.Println(p[0:3]) // => [0 10 20]
}
In my code example above, "p[0:3]" would seem to intuitively "read" as: "the slice from position 0 to position 3", equating to [0, 10, 20, 30]. But of course, it actually equates to [0 10 20].
So my question is: what is the design rationale for the upper value evaluating to hi-1 rather than simply hi? It feels unintuitive, but there must be some reason for it that I'm missing, and I'm curious what that might be.
Thanks in advance.
This is completely a matter of convention, and there are certainly other ways to do it (for example, Matlab uses arrays whose first index is 1). The choice really comes down to what properties you want. As it turns out, using 0-indexed arrays where slicing is inclusive-exclusive (that is, a slice from a to b includes element a and excludes element b) has some really nice properties, and thus it's a very common choice. Here are a few advantages.
Advantages of 0-indexed arrays and inclusive-exclusive slicing
(note that I'm using non-Go terminology, so I'll talk about arrays in the way that C or Java would talk about them. Arrays are what Go calls slices, and slices are sub-arrays (ie, "the slice from index 1 to index 4"))
Pointer arithmetic works. If you're in a language like C, arrays are really just pointers to the first element in the array. Thus, if you use 0-indexed arrays, then you can say that the element at index i is just the element pointed at by the array pointer plus i. For example, if we have the array [3 2 1] with the address of the array being 10 (and assuming that each value takes up one byte of memory), then the address of the first element is 10 + 0 = 10, the address of the second is 10 + 1 = 11, and so on. In short, it makes the math simple.
The length of a slice is also the place to slice it. That is, for an array arr, arr[0:len(arr)] is just arr itself. This comes in handy a lot in practice. For example, if I call n, _ := r.Read(arr) (where n is the number of bytes read into arr), then I can just do arr[:n] to get the slice of arr corresponding to the data that was actually written into arr.
Indices don't overlap. This means that if I have arr[0:i], arr[i:j], arr[j:k], arr[k:len(arr)], these slices fully cover arr itself. You may not often find yourself partitioning an array into sub-slices like this, but it has a number of related advantages. For example, consider the following code to split an array based on non-consecutive integers:
func consecutiveSlices(ints []int) [][]int {
ret := make([][]int, 0)
i, j := 0, 1
for j < len(ints) {
if ints[j] != ints[j-1] + 1 {
ret = append(ret, ints[i:j])
i = j
}
}
ret = append(ret, ints[i:j])
}
(this code obviously doesn't handle some edge cases well, but you get the idea)
If we were to try to write the equivalent function using inclusive-inclusive slicing, it would be significantly more complicated.
If anyone can think of any more, please feel free to edit this answer and add them.
The Go Programming Language Specification
Slice types
Slice expressions
For a string, array, pointer to array, or slice a, the primary
expression
a[low : high]
constructs a substring or slice. The indices low and high select which
elements of operand a appear in the result. The result has indices
starting at 0 and length equal to high - low.
For convenience, any of the indices may be omitted. A missing low
index defaults to zero; a missing high index defaults to the length of
the sliced operand
For arrays or strings, the indices are in range if 0 <= low <= high <=
len(a), otherwise they are out of range. For slices, the upper index
bound is the slice capacity cap(a) rather than the length. A constant
index must be non-negative and representable by a value of type int;
for arrays or constant strings, constant indices must also be in
range. If both indices are constant, they must satisfy low <= high. If
the indices are out of range at run time, a run-time panic occurs.
For q := p[m:n], q is a slice of p starting at index m for a length of n-m elements.