为什么Golang要求花括号不能在下一行?

45

正确:

if(true) {

}

不正确:

if(true)
{

}

这种样式为什么被强制执行?是因为与语言规范有关还是只是因为他们更喜欢其中一种风格?


4
Go语言的编译器会自动为你输入所有的分号。在错误的版本中,它会在if(true);{}之后添加分号,像这样是不正确的。 - Akshay Deep Giri
2
第一个版本在语法上是正确的,但风格很糟糕。应该只在需要时使用 ()。见 Effective Go - deft_code
3
@deft_code这完全没有意义。如果需要使用(),你怎么可能不使用呢?这是自相矛盾的。 - Neutrino
2
@deft_code,你写成了“应该”而不是“不应该”。 - Ryan Haining
2
@Neutrino,同样的道理也适用于将C中的“{”替换为“then”,将“}”替换为“end”。然后你就可以编写可读性强的C代码了。我认为Go的设计者做出了一个伟大的决定,结束了神圣的风格之争。Go应该只有一种风格,而且它只有一种风格。它的大小应该与计数的大小相同。阿门。这个想法已经深入人心,自从Go在Google变得流行以来,我们使用的大多数语言都编写了自动格式化程序。我不再想念能够自定义我的代码缩进/空格风格的时代了。 - deft_code
显示剩余3条评论
5个回答

46
为什么代码块用大括号而不是分号?为什么我不能把左花括号放在下一行?
Go使用大括号来进行语句组合,这种句法对于以C语言家族中的任何语言编程的程序员来说都很熟悉。然而,分号是给解析器用的,而不是给人用的,我们想尽可能地减少它们的使用。为了实现这个目标,Go借鉴了BCPL的一个技巧:分号符在正式语法中用于分隔语句,但是由词法分析器在任何可能是语句结尾的行的末尾自动注入,而不需要预先查看输入。这种做法在实践中非常有效,但会导致强制采用一种大括号风格。例如,函数的左花括号不能出现在单独的行上。 http://golang.org/doc/faq#semicolons

87
如果它强制使用大括号风格,这反而会破坏最初使用大括号的目的(即通过在同一列上排列大括号来轻松识别语句块的范围),那么很难将其描述为“工作得非常好”。 就我个人而言,如果这意味着我可以合理地对齐大括号,我更愿意手动添加分号。这是治疗比疾病更糟糕的典型例子。 - Neutrino
13
我猜Go语言的设计者认为强制编写规范是一件好事。"限制能够释放创造力"。 - maerics
16
他们的借口是无效的。Lua早在Go诞生之前就已经使用分号来表示代码块,而且不需要分号。他们可以支持其他括号样式,但他们明确选择不这样做。无论是为了让他们通过解析骗过检查还是因为他们决定强制将个人喜好强加给用户,这两个原因都不是将用户限制在单一括号风格中的好理由。 - Pharap
4
@Pharap: 更不用说,新行上的大括号是符合ANSI标准的。K&R则不是。但是标准的美妙之处就在于有如此多种不同的标准。Internet Explorer就是一个绝佳的例子。另外,为了让代码更容易被打印而强制实行某种风格是很愚蠢的。他们应该实行一种最大程度上阻止你打印代码的风格。打印代码是愚蠢的(你想要实现什么?备份吗?),并且这是一个安全风险,更不用说环境问题了。此外,如果你想要特定格式的代码,请编写一个代码美化工具。 - Stefan Steiger
4
@StefanSteiger 嗯,我有时候会将代码打印出来,因为这样更容易进行调试、解决复杂问题,或者直接用红笔标注。你对此的看法过于苛刻和主观了。 #仅供参考。 - Jim
显示剩余4条评论

28

大多数派生自C语言的编程语言使用这种风格:if ( <condition> ) <statement>,当condition为真时执行statementstatement可以是单个语句或用括号括起来的块。

Go语言的if语句需要一个后面用括号括起来的块而不是单个语句。这是为了避免一种常见错误,大多数编码规范都要求所有的if语句都要用括号括起来。

//subtle error in C
if (<condition>)
  <statement1>;
  <statement2>;
现在Go语言要求在if语句后面加上括号,这使得()变得冗余。它们只是用来帮助词法分析器区分条件和语句,否则if 很难解析。(条件从哪里结束,语句从哪里开始?)
现在Go的作者需要做出一个决定:
- 保留冗余的() - 要求在后面增加{
他们决定不希望有冗余。这也产生了第二个副作用。由于每个换行符都会有一个隐式的;,如果{在下一行,则会在和{之间放置一个;。Go的作者再次面临一个决策:
- 特殊处理解析器以更智能地处理; {构造 - 要求所有人采用相同的if ... {样式在同一行上。 - 要求在单独一行上。
特殊处理解析器是非常糟糕的事情。看看速度D和Go解析器与C++糟糕的解析器性能相比如何。另外,统一的风格是一个好事情。考虑到约束条件,他们的最终决定是相当简单的。

1
为什么要与D和C++进行比较?它们没有隐式的; - jokoon
Go语言的;插入使用了非向前查看的词法分析器,这样做非常快。出于性能考虑,隐式的;和显式的;一样快。D、C++、Java、C#和Go都在它们的语法规范中使用了;,它们都是C的后代(也称花括号语言)。我将它们进行比较,因为它们的解析器相似并且必须解决类似的问题。 - deft_code
4
为什么我们不直接编写机器代码?编程语言的目的是使程序易于被人类阅读,而非机器。编译器的作用是生成机器能高效执行的代码。我不在意解析速度慢这点。那些括号并没有帮助人眼理解。Python 在这方面更加连贯。 - Yugo Amaryl
1
花括号确实有助于人眼的阅读,而Python的显著空格则是一个糟糕的设计选择。程序员不应该自己缩进代码,这应该由代码格式化工具自动完成。 - saolof

8
这与规范有关,也就是说,这不仅是他们在编译器中构建的东西。 分号 正式语法在多个产生式中使用分号 ";" 作为终止符。Go程序可以使用以下两个规则省略大部分这些分号:
当输入被分成标记时,如果行的最后一个标记是
- 标识符 - 整数、浮点、虚数、符文或字符串文字之一 - 关键字 break、continue、fallthrough 或 return 之一 - 运算符和分隔符 ++、--、)、] 或 }
则自动将分号插入到非空行的标记流的末尾。
为了允许复杂的语句占据单行,可以在")"或"}"关闭之前省略分号。
为了反映惯用法,本文档中的代码示例使用这些规则省略分号。
根据我从他们的演讲中所理解的,他们希望摆脱格式讨论,并通过创建gofmt来扩展这个想法。

1
如果正式语法使用分号,为什么要让人们有选择省略它们的选项呢?在我看来,他们为了迎合那些懒得按一个键的人而牺牲了清晰度... - Kenny Worden
1
我认为Go语言对“源代码的格式化方式”的严格要求是该语言的一大优点。这使得我们很容易、也很愉悦地深入了解别人的代码,尤其是库文件。但在其他编程语言(例如JavaScript)中进行同样的操作时,通常感觉就像是要学习一个“新”的子集一样。该语言的作者旨在解决由10,000名程序员共同维护的数十亿行代码库所带来的问题,即Google规模的编程。常见问题更多地是要坚持一种格式化方式,而不是找到“正确的方式”。 - tike

3
因为谷歌的人不喜欢allman风格。 但是支持allman风格非常容易,forkGo(由@diyism)只在golang编译器中添加了12行代码。
试试forkGo: https://gofork.org forkGo支持这种allman风格:
package main
import
(
    "fmt"
)


func main()
{
    if false
    {   fmt.Println("jack")
        fmt.Println("forkgo")
    } else
    {   fmt/
           .Println("hello")
        fmt.Println("forkgo")
    }
}

新手问题,但是在不同的行上,fork支持else吗? - Xam

0

“使用大括号的目的在于方便通过将大括号对齐在同一列上来识别语句块的作用域。”

“同意 - 如果你喜欢将大括号对齐以区分块,则会非常困难。”


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