Почему в Go можно переопределить ошибку в операторе множественного возврата

Рассмотрим следующий пример, иллюстрирующий вопрос (он был создан только для того, чтобы пояснить вопрос, но я видел похожий код и в книгах, и в реальных проектах):

package main

import (
    "strconv"
    "fmt"
    "log"
)

func main() {
    n1, err := strconv.Atoi("1")
    if err != nil {
        log.Panicf("%v", err)
    }

    n2, err := strconv.Atoi("2")
    if err != nil {
        log.Panicf("%v", err)
    }

    // err := fmt.Errorf("new error") <- line 1

    // n1, err := strconv.Atoi("3") <- line 2

    fmt.Printf("n1 = %d, n2 = %d\n", n1, n2)
}

Компилятор не жалуется на переопределение err, но если я раскомментирую <- line 1 или <- line 2, он будет жаловаться no new variable on left side of :=.

Итак, как это работает? Почему компилятор с радостью позволяет переопределить err в операторе множественного возврата, используя :=, но не n1 в примере <- line 2?

Лучше, если вы можете указать на официальную ссылку, объясняющую это поведение.


person Aliaksandr Kazlou    schedule 14.12.2015    source источник
comment
Ну, я почти закончил gopl.io (хотя сосредоточился на типах данных, методах, интерфейсах, параллелизме), и читайте эффективный Go и другие ресурсы (хотя обычно это темы, которые меня интересуют в данный момент), я использовал Go для алгоритмических соревнований, а также для создания инструмента CLI. Я имею в виду, что чтение документации — это хорошо, но гораздо лучше создавать что-то и при этом изучать язык, для меня это баланс между затратами времени на обучение и созданием (получением результата) вещей. Особенно, когда вы начинаете изучать новый язык, вы хотите видеть результаты, чтобы продолжать его.   -  person Aliaksandr Kazlou    schedule 14.12.2015
comment
И когда я говорю о CLI, я имею в виду использование crypto/ssh/net/http/base64/yaml и других пакетов, а также go-bindata :), просто общий обзор :)   -  person Aliaksandr Kazlou    schedule 14.12.2015
comment
Но спасибо, что упомянули об этом в эффективном документе go.   -  person Aliaksandr Kazlou    schedule 14.12.2015


Ответы (1)


Это потому, что вы использовали краткое объявление переменной :=. Цитата из спецификации:

В отличие от обычных объявлений переменных, короткое объявление переменной может повторно объявлять переменные при условии, что они были первоначально объявлены ранее в том же блоке (или в списках параметров, если блок является телом функции) с тем же типом и, по крайней мере, одна из не-пустых переменных является новой. Как следствие, повторное объявление может появиться только в коротком объявлении с несколькими переменными. Повторное объявление не вводит новую переменную; он просто присваивает новое значение оригиналу.

Эта строка:

n1, err := strconv.Atoi("1")

Является кратким объявлением с несколькими переменными, и все переменные в левой части являются новыми, поэтому все они будут объявлены (и назначены возвращаемые значения strconv.Atoi()).

Эта строка:

n2, err := strconv.Atoi("2")

Это короткое объявление с несколькими переменными, и n2 является новым. Таким образом, он объявляет n2 и присваивает новое значение только err, потому что err уже объявлено в том же блоке.

Эта строка:

err := fmt.Errorf("new error") <- line 1

Это не короткое объявление с несколькими переменными. Он попытается объявить err, но он уже объявлен в том же блоке, поэтому это ошибка времени компиляции.

И эта строка:

n1, err := strconv.Atoi("3") <- line 2

Это короткое объявление с несколькими переменными, но все переменные в левой части были ранее объявлены в том же блоке, поэтому это также ошибка времени компиляции ( он не вводит никаких новых переменных в левой части).

Обратите внимание, что если все переменные в левой части были ранее объявлены, просто измените объявление короткой переменной := на Assignment = заставит его работать (предполагаемые значения справа назначаются переменным на левая сторона).

person icza    schedule 14.12.2015