2014-07-02 63 views
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) 
     } 
+0

'name'在循环之前被声明,否则它将被限制到if块并且不在该块之外的范围内(阅读:以后不能使用它)。循环不会重新初始化它;它只是重新使用它。块(循环)位于变量声明之后。 – elithrar

+0

只是想提一下''name''实际上是在循环之内声明的(在sql文档中)。在我看来,我错过了块范围的东西。我不知道''if''(也可能是''for'')块具有这种隔离属性 – hey

+0

@hey:它包含在[The Go Programming Language Specification](http://golang.org/ref/spec):[Blocks](http://golang.org/ref/spec#Blocks)。 – peterSO

回答

3

除非你已经确定的分配是一个性能瓶颈,我不会考虑这样一个不成熟的优化。毕竟,它甚至可能没有什么区别,所以最好在可读性/可维护性方面犯错。

一般来说,我建议使用最小范围的变量是有道理的。如果它们被分配了堆栈,那么它们将非常便宜 - 假设空间可用,它可能只是将变量初始化为零或其初始值。堆栈分配的变量在一个循环中的作用域可能最终每次通过循环时都会有相同的内存位置,所以将它们移出它们并没有多少好处。

就是这样,当一个变量被分配到堆栈上时,并不总是很明显。如果编译器确定传递给row.Scan的指针可能会被保留在函数调用之后(即它的转义为),那么name将被分配到堆上,即使它已被定义为var

同样,如果转义分析确定该变量不能转义,则创建带有new的字符串变量的版本可能决定将其放置在堆栈上。

+0

这有点烦人。例如,我有一些准备好的语句,所以如果使用不当(即缓存),性能将得到改善。但是,如果在每个循环中初始化一个新的连接/语句使其无用。我想在循环中声明它们,但有时我会得到“连接太多”的错误,所以我只能假设Go在每个循环中初始化它们,因此对于SQL示例的混淆。 – hey

+0

如果你想在循环的迭代之间使用相同的值,那么在循环之外声明变量是有意义的。在你的问题,但你有一个变量只用于循环。 –

相关问题