Переменная Go не перезаписывается в инструкции if

#go

#Вперед

Вопрос:

 duration := 1 * time.Hour
if true {
    duration, err := time.ParseDuration("5s")
    _ = duration // if I don't have this, it gives me a duration declared not used error
    if err != nil {
        log.Fatal(err)
    }
}

fmt.Println(duration) // prints 1 hour
  

Я думаю, проблема здесь в том, что duration снова объявляется как local var в инструкции if. Есть ли синтаксически хороший способ решить эту проблему?

Комментарии:

1. golang.org/ref/spec#Declarations_and_scope

Ответ №1:

Действительно, вы снова объявляете duration переменную в if блоке. Мой способ сделать это — объявить err перед if блоком (обычно в начале функции, поскольку error переменная практически всегда необходима …).

 var err error
duration := 1 * time.Hour
if true {
    duration, err = time.ParseDuration("5s")
    if err != nil {
        log.Fatal(err)
    }
}
  

Комментарии:

1. Да, я пробовал это, это сработало. Но оператор if не должен быть замыканием, подобным вызову функции, я чувствую, что это должна быть ошибка, которую golang должен исправить сам?

2. Это не ошибка, такое поведение соответствует спецификациям. Об этом уже сообщалось как о проблеме Golang, но она была отклонена по этой причине: code.google.com/p/go/issues/detail?id=5990

3. Болен! Я думаю, они просто хотят быть очень строгими к этому языку. Это действительно полезно 🙂

Ответ №2:

Вопрос в том, хотите ли вы, чтобы она duration перезаписывалась, когда time.ParseDuration возвращает ошибку, или нет. Если нет, то я бы написал

 duration := 1 * time.Hour
if true {
    d, err := time.ParseDuration("5s")
    if err != nil {
        log.Fatal(err)
    } else {
        duration = d
    }
}
  

Если вас не волнует старое значение в случае ошибки, ответ @ julienc так же хорош.

Комментарии:

1. То есть каждый раз, когда мне нужно объявлять новый var? Я чувствую, что это должно быть довольно распространенным логическим вариантом использования в любом коде…

Ответ №3:

Go — это блочно-структурированный язык программирования. Как правило, переменные объявляются с минимальной областью видимости.

Переменная duration объявляется и используется во внешней ( func ) области видимости, а также устанавливается во внутренней ( if ) области видимости. err Переменная объявляется и используется во внутренней ( if ) области видимости. Например,

 package main

import (
    "fmt"
    "log"
    "time"
)

func main() {
    duration := 1 * time.Hour
    if true {
        var err error
        duration, err = time.ParseDuration("5s")
        if err != nil {
            log.Fatal(err)
        }
    }
    fmt.Println(duration)
}
  

Вывод:

 5s
  

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

 func main() {
    duration := 1 * time.Hour
    if true {
        duration, err := time.ParseDuration("5s")
        if err != nil {
            log.Fatal(err)
        }
    }
    fmt.Println(duration)
}
  

Ошибка:

 duration declared and not used
  

Переменная duration объявляется и используется во внешней ( func ) области видимости. Она также объявлена (не объявлена повторно) и установлена, но не используется, во внутренней ( if ) области видимости.

Ссылки:

Блок (программирование)

Область действия блока (информатика)

Спецификация языка программирования Go

Спецификация языка программирования Go: блокирует

Спецификация языка программирования Go: объявления и область видимости

Спецификация языка программирования Go: короткие объявления переменных