Почему не удается выполнить компиляцию else if x: = x + 1, даже если в левой части: = не определена новая переменная - PullRequest
3 голосов
/ 05 августа 2020

Ожидается, что следующий код завершится с ошибкой при компиляции:

package main

import (
    "fmt"
)

func main() {
    x := 10
    x := x + 1
    fmt.Println(x)
}

Ошибка компиляции:

./prog.go:9:4: no new variables on left side of :=

Итак, я ожидал, что этот код также выйдет из строя с ошибкой :

package main

import (
    "fmt"
)

func main() {
    if x := 10; x < 10 {
        fmt.Println("if block: x:", x)
    } else if x := x + 1; x < 20 {
        fmt.Println("else if block: x: ", x)
    }
}

Вот результат:

else if block: x:  11

Почему вторая программа завершается успешно, даже если оператор := в else if x := x + 1 не определяет никаких новых переменных?

Ответы [ 3 ]

12 голосов
/ 05 августа 2020

Из спецификаций Go вот как определяется оператор if:

IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .

Позже, в объявлениях и области В разделах сказано:

Идентификатор, объявленный в блоке, может быть повторно объявлен во внутреннем блоке. Хотя идентификатор внутреннего объявления находится в области видимости, он обозначает объект, объявленный внутренним объявлением.

Итак, теперь оператор if является неявным блоком :

Каждый оператор «if», «for» и «switch» считается находящимся в собственном неявном блоке.

Тогда, как вы можете видеть из IfStmt определение, после ключевого слова else может снова идти IfStmt, что, будучи неявным блоком внутри другого IfStmt (все еще неявным блоком), удовлетворяет условию повторного объявления идентификатора.

Сравните также с явными блоками:

func foo() {
    x := 10
    {
        x := 20
        fmt.Println("Inner x:", x) // 20
    }
    fmt.Println("Outer x:", x) // 10
}
0 голосов
/ 13 августа 2020

Вы имеете дело с двумя разными переменными x из-за того, что ваша вторая x: = находится в другой области. Таким образом, для вас это выглядит как один и тот же x, но это не так, даже если оно основано на значении внешнего x, это не влияет на него. X, который вы печатаете, является внутренним x.

Рассмотрим этот пример :

package main

import (
    "fmt"
)

func main() {

    // Define x in the func scope
    x := 10
    // Print out global scope x
    fmt.Printf("x1:%v\n", x)

    // Not allowed (x already defined in this scope)
    //x := x + 1

    // Allowed (different x)
    {
        x := x + 1
        // Print new local scope x (this is a second x)
        fmt.Printf("x2:%v\n", x)

    }

    // Allowed (different x defined)
    if x := x + 1; x > 10 {
        // Print new local scope x (this is a third x)
        fmt.Printf("x3:%v\n", x)
    }

    // Print out global scope x
    fmt.Printf("x1:%v\n", x)
}

В этом примере у вас есть 3 x переменных. Забавный c уровень первый, первый в пределах {}, затем другой (снова независимый) в блоке if. Все три из них независимы, и два внутренних затеняют внешний после того, как они определены (в этой области), поэтому даже если вы выберете основание x 2 и 3 на начальном x, они не повлияют на его значение.

Вы можете увидеть это, когда распечатываете глобальную область видимости x в конце, и поскольку x3 не зависит от значения x2, мы оказываемся в конце функции:

  • x1: 10
  • x2: 11
  • x3: 11
0 голосов
/ 05 августа 2020

В этом примере

func main() {
    if x := 10; x < 10 {
        fmt.Println("if block: x:", x)
    } else if x := x + 1; x < 20 {
        fmt.Println("else if block: x: ", x)
    }
}

вы определяете переменную x в двух местах, что является правильным, поскольку область действия этой переменной находится в пределах if и else. Рассмотрим этот код следующим образом:

введите описание изображения здесь

Если вы видите, что у нас есть два блока, блок 1 и блок 2

Оба блока имеют свою собственную область видимости, и все, что вы определяете внутри этого блока, не будет доступно извне.

Попробуйте это, и вы получите сообщение об ошибке.

func main() {
    if x := 10; x < 10 {
        fmt.Println("if block: x:", x)
    } else if x := x + 1; x < 20 {
        fmt.Println("else if block: x: ", x)
    }
    fmt.Println("What is the value of x: ", x)
}

error: ./prog.go:13:45: undefined: x

Потому что вы пытаются получить доступ к этой переменной извне.

...