在什么情况下应该初始化一个新变量,而在什么情况下不应该初始化?

huangapple go评论70阅读模式
英文:

When should you initialize a new variable and when you should not?

问题

我看了一下代码示例,并对变量的初始化方式感到有些困惑。据我理解,var关键字用于初始化变量,但如果已经有这样的变量,最好是“重用”它而不是重新初始化它。我意识到我可能误解了Golang的规范,所以我希望这个问题能帮助我(也许是其他人)理解正确。

为什么在循环内部初始化"name"变量,而不是在循环外部?(见下面的代码)。每次循环重新初始化它不是效率更低吗?

//我会这样做
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var name string //在循环外部
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) //在循环外部使用指针
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)
}

英文:

I'm looking at the code examples sql.query and i'm a bit confused by the way the variables are initialized. As far as I understand the var keyword initialize the variable but if you already have a such variable it's better to 'reuse' it instead to reinitialize it. I'm aware that I might have misunderstood the golang specs so I hope this question would help me (and perhaps other folks) get it right.

  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)
    }

Why is the "name" variable initialized within the loop and not outside the loop ? (see below). Isn't it less performant to reinitialize it on each loop ?

//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)
    }

or even better use a pointer

      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)
        }

答案1

得分: 3

除非你确定分配是性能瓶颈,否则我不会考虑这样的过早优化。毕竟,它可能并没有什么区别,所以最好以可读性/可维护性为重。

一般来说,我建议在变量的作用域中使用最小的范围。如果它们是栈分配的,那么它们的成本会很低——假设有空间可用,可能只涉及将变量初始化为零或其初始值。在循环内部作用域的栈分配变量可能每次循环都会有相同的内存位置,所以将它们移出来并没有太多好处。

话虽如此,变量是否在栈上分配并不总是明显的。如果编译器决定传递给row.Scan的指针可能会在函数调用之后保留(即逃逸),那么name将在堆上分配,即使它是用var定义的。

同样,如果逃逸分析确定变量不会逃逸,使用new创建字符串变量的版本可能会决定将其放在栈上。

英文:

Unless you have determined that the allocation is a performance bottleneck, I wouldn't consider such a premature optimisation. After all, it might not even make a difference, so it is best to err on the side of readability/maintainability.

In general, I'd suggest using the smallest scope for your variables that makes sense. If they are stack allocated, then they will be quite cheap -- assuming space is available, it probably just involves initialising the variable to zero or its initial value. Stack allocated variables scoped within a loop will probably end up with the same memory location each time through the loop too, so there isn't much to be gained from moving them out.

With that said, it isn't always obvious when a variable will be allocated on the stack. If the compiler decides that the pointer passed to row.Scan could possibly be retained past the function call (that is, it escapes), then name will be allocated on the heap even though it has been defined with var.

Similarly if the escape analysis determines that the variable doesn't escape, the version that creates the string variable with new may decide to place it on the stack.

huangapple
  • 本文由 发表于 2014年7月3日 04:09:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/24540158.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定