Search code examples
dictionarygosyntaxsemanticsassignment-operator

What semantic rules in Go decide when a one-value assignment occurs vs. when a two-value assignment occurs?


While studying map from A Tour of Go: Mutating Maps, one thing I found surprising was that we can access the value for a key in a map using either one-value assignment or two-value assignment. Example code:

package main

import (
    "fmt"
)

func main() {
    m := map[int]int{2: 4, 3: 9, 4: 16}
    
    // Example 1
    fmt.Println(m[2])
    
    // Example 2
    v := m[2]
    fmt.Println(v)
    
    // Example 3
    v, ok := m[2]
    fmt.Println(v, ok)
}

Output:

4
4
4 true

What semantic rules are involved in supporting both one-value and two-value assignments using the same syntax? Are there other such special forms in Go that support both one-value and two-value assignments in the same syntax depending on the left-hand side of the assignment operator?

Further, can I write a function foo() myself that can return either one-value or two-values depending on the left-hand side of the assignment operator?


Solution

  • The one-or-two value assignment for map index operations is a special form provided as a convenience, it unfortunately cannot be done in "normal" assignments.

    Normal assignment expressions:

    The spec has the following to say about tuple 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 = '一', '二', '三'

    This leaves no room for ambiguity about the number of values in an assignment.

    One, or two-value expressions:

    There are 4 cases where both one and two values are allowed on the left side of the expression. Three of them are special forms of assignment expressions, the last is the range clause.

    Index expressions:

    Index expressions are defined as being of the form a[x], with the notable exception of maps:

    An index expression on a map a of type map[K]V used in an assignment or initialization of the special form

    v, ok = a[x]
    v, ok := a[x]
    var v, ok = a[x]
    

    yields an additional untyped boolean value.

    Receive operators:

    The same happens with the Receive Operator which is normally of the form x <-ch:

    A receive expression used in an assignment or initialization of the special form

    x, ok = <-ch
    x, ok := <-ch
    var x, ok = <-ch
    var x, ok T = <-ch
    

    yields an additional untyped boolean result reporting whether the communication succeeded.

    Type Assertions:

    Once more the mention of special form in type assertions, normally of the form x.(T):

    A type assertion used in an assignment or initialization of the special form

    v, ok = x.(T)
    v, ok := x.(T)
    var v, ok = x.(T)
    var v, ok T1 = x.(T)
    

    yields an additional untyped boolean value.

    Range clause:

    The for statement with range clause has looser language associated with it because it is not a modification of the normal assignment expression:

    Function calls on the left are evaluated once per iteration. For each iteration, iteration values are produced as follows if the respective iteration variables are present:

    Range expression                          1st value          2nd value
    
    array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
    string          s  string type            index    i  int    see below  rune
    map             m  map[K]V                key      k  K      m[k]       V
    channel         c  chan E, <-chan E       element  e  E
    

    Uses for non-assignments:

    As mentioned above, all three special forms are for assignments only. Attempting to use multi-value returns in other expressions (function call, return, etc...) will fail as these are not assignments and do not benefit from the special form.