何时应该初始化新变量,何时不应该?

3

我正在查看代码示例sql.query,但对变量初始化的方式感到有些困惑。据我所知,var关键字用于初始化变量,但是如果您已经有这样的变量,最好“重用”它而不是重新初始化它。我知道我可能误解了Golang规范,因此希望这个问题可以帮助我(也许还有其他人)理解得更清楚。

  rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
    if err != nil {
            log.Fatal(err)
    }
    defer rows.Close()
    for rows.Next() {
            var name string
            if err := rows.Scan(&name); err != nil {
                    log.Fatal(err)
            }
            fmt.Printf("%s is %d\n", name, age)
    }
    if err := rows.Err(); err != nil {
            log.Fatal(err)
    }

为什么"名称"变量在循环内初始化而不是循环外?(见下文)。重复初始化它不会降低性能吗?
//how I would do this
  rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
    if err != nil {
            log.Fatal(err)
    }
    defer rows.Close()
    var name string //outside the loop
    for rows.Next() {

            if err := rows.Scan(&name); err != nil {
                    log.Fatal(err)
            }
            fmt.Printf("%s is %d\n", name, age)
    }
    if err := rows.Err(); err != nil {
            log.Fatal(err)
    }

或者更好的方法是使用指针。

      rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
        if err != nil {
                log.Fatal(err)
        }
        defer rows.Close()
        name := new(string) //pointer outside the loop
        for rows.Next() {

                if err := rows.Scan(name); err != nil {
                        log.Fatal(err)
                }
                fmt.Printf("%s is %d\n", name, age)
        }
        if err := rows.Err(); err != nil {
                log.Fatal(err)
        }

name在循环之前被声明,否则它将被限定在if块内,在该块之外无法使用它(即:您不能在之后使用它)。循环不会重新初始化它,它只是重复使用它。该块(循环)位于变量声明之后。 - elithrar
我只想提一下,“name”实际上是在循环内部声明的,而不是之前(在SQL文档中)。对我来说,似乎我错过了块作用域的东西。我不知道“if”(也许还有“for”)块有这种孤立属性。 - hey
@嘿:这个在《Go编程语言规范》(http://golang.org/ref/spec)中有详细介绍:[Blocks](http://golang.org/ref/spec#Blocks)。 - peterSO
1个回答

3
除非你确定分配是性能瓶颈,否则我不会考虑这样的过早优化。毕竟,它可能并没有实质性作用,所以最好从可读性和可维护性方面出发。
总体上,我建议只使用最小范围来定义变量。如果它们被分配到堆栈中,那么它们的成本将非常低-假设空间是可用的,它可能只涉及将变量初始化为零或其初始值。在循环内部作用域的堆栈分配变量可能每次通过循环都会得到相同的内存位置,因此无法从将它们移出中获益。
尽管如此,变量何时被分配到堆栈中并不总是明显的。如果编译器认为传递给row.Scan的指针可能在函数调用后保留(也就是说,它“逃逸”了),那么即使使用var定义,name也将被分配到堆中。
同样,如果逃逸分析确定变量不会逃逸,创建具有new的字符串变量的版本可能会决定将其放在堆栈中。

有点烦人。例如,我有一些准备好的语句,如果它们没有被正确使用(即缓存),那么性能肯定会提高。但是,如果在每个循环中初始化一个新连接/语句,那么它们就变得无用了。我想在循环内部声明它们,但有时会出现“连接过多”的错误,因此我只能假设Go在每个循环中都会初始化它们,这就是关于go SQL示例的困惑所在。 - hey
如果您希望在循环迭代之间使用相同的值,最好将变量声明在循环之外。然而,在您的问题中,您只在循环内使用了变量。 - James Henstridge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接