Slices and multiple assignment in the Go Programming Language

时间:2018-02-26 17:43:32

标签: go variable-assignment slice

According to the "Assignments" section of the Go Language Spec:

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.

While playing with slices and attempting to implement a simple "delete" (or "splice") operation I see the following confusing behavior (Go Playground):

xs := []int{1, 2, 3, 4, 5}
x := xs[2]
xs = append(xs[:2], xs[3:]...)
fmt.Printf("OK: x=%d, xs=%#v\n", x, xs)

ys := []int{1, 2, 3, 4, 5}
y, ys := ys[2], append(ys[:2], ys[3:]...)
fmt.Printf("??: y=%d, ys=%#v\n", y, ys)

// OK: x=3, xs=[]int{1, 2, 4, 5}
// ??: y=4, ys=[]int{1, 2, 4, 5}

Why do the separate assignments to "x" and "xs" work as expected in the first example but the multi-assignment to "y" and "ys" appears to evaluate out of order? Is it related to the implicit define-and-assign combination?

2 个答案:

答案 0 :(得分:2)

The Go Programming Language Specification

Assignments

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.

Order of evaluation

At package level, initialization dependencies determine the evaluation order of individual initialization expressions in variable declarations. Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.

For example, in the (function-local) assignment

y[f()], ok = g(h(), i()+x[j()], <-c), k()

the function calls and communication happen in the order f(), h(), i(), j(), <-c, g(), and k(). However, the order of those events compared to the evaluation and indexing of x and the evaluation of y is not specified.


Follow the link (evaluated in the usual order), that you should have included in your quote, to the Order of Evaluation section.

For your example, evaluate the append function first:

package main

import (
    "fmt"
)

func main() {
    xs := []int{1, 2, 3, 4, 5}
    x := xs[2]
    xs = append(xs[:2], xs[3:]...)
    fmt.Printf("OK: x=%d, xs=%#v\n", x, xs)

    ys := []int{1, 2, 3, 4, 5}
    y, ys := ys[2], append(ys[:2], ys[3:]...)
    fmt.Printf("OK: y=%d, ys=%#v\n", y, ys)

    {
        ys := []int{1, 2, 3, 4, 5}

        // phase 1
        t1 := append(ys[:2], ys[3:]...)
        t2 := ys[2]

        // phase 2
        y = t2
        ys = t1

        fmt.Printf("OK: y=%d, ys=%#v\n", y, ys)
    }

    // OK: x=3, xs=[]int{1, 2, 4, 5}
    // OK: y=4, ys=[]int{1, 2, 4, 5}
}

Playground: https://play.golang.org/p/n9f0qhZadUr

Output:

OK: x=3, xs=[]int{1, 2, 4, 5}
OK: y=4, ys=[]int{1, 2, 4, 5}
OK: y=4, ys=[]int{1, 2, 4, 5}

答案 1 :(得分:0)

作为noted by @peterSO,根据"Order of evaluation" section of the spec

  

... [函数调用] 的顺序与 [索引表达式] 的评估和索引相比未指定。

这意味着,对于上面的示例代码,append(...)函数调用碰巧发生在索引表达式之前,因为未指定这些操作的相对顺序。

如果在函数调用中包装索引表达式,则操作按预期顺序进行:

idint := func(x int) int { return x }

zs := []int{1, 2, 3, 4, 5}
z, zs := idint(zs[2]), append(zs[:2], zs[3:]...)

fmt.Printf("OK: z=%d, zs=%#v\n", z, zs)
// OK: z=3, zs=[]int{1, 2, 4, 5}