Go 语言中关于断行规则的一个细节 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
liulaomo
V2EX    Go 编程语言

Go 语言中关于断行规则的一个细节

  •  
  •   liulaomo 2019-05-08 20:35:59 +08:00 5047 次点击
    这是一个创建于 2399 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先以一个小问题开头。请问下面这个程序的编译运行将表现出何种行为?三个选项:

    1. 编译不通过;
    2. 打印输出“真”;
    3. 打印输出“假”。
    package main func alwaysFalse() bool { return false } func main() { switch alwaysFalse() { case true: println("真") case false: println("假") } } 

    (先思考一下,答案需翻页。)

    OK,公布答案。答案是 2,打印输出“真”。答对的可随意继续或返回。答错的同志继续阅读下面的解释。

    有些答错的同志可能觉得此程序编译不过,因为switch代码块的开大括号{被放在了下一行,这在 Go 中时不允许的。其实非也,其实 Go 语言的断行规则定义如下:

    1. 在 Go 代码中,注释除外,如果一个代码行的最后一个语法词段( token )为下列所示之一,则一个分号将自动插入在此字段后(即行尾):
    • 一个标识符;
    • 一个整数、浮点数、虚部、码点或者字符串字面表示形式;
    • 这几个跳转关键字之一:breakcontinuefallthroughreturn
    • 自增运算符++或者自减运算符--
    • 一个右括号:)]}
    1. 为了允许一条复杂语句完全显示在一个代码行中,分号可能被插入在一个右小括号)或者右大括号}之前。

    很多人印象中的规则“代码块的开大括号不能被放在下一行”其实只是一个通常的而非普适的规则。根据上述规则,上面这个程序中的 switch 代码块在编译前将被改写为如下所示(注意插入的几个分号):

     switch alwaysFalse(); { case true: println("真"); case false: println("假"); }; 

    另外有些答错的同志可能觉得此程序运行时应该打印输出“假”。这里,我们还需要知道 Go 中关于switch代码块的另一个常识:switch代码块中开大括号{前的比较表达式可以省略,其默认值为true。因此上面这个switch代码块将被继续改写为如下所示:

     switch alwaysFalse(); true { case true: println("真"); case false: println("假"); }; 

    到此为止,我们可以清楚地得知此程序为什么会打印输出“真”了。

    结尾展示另外一段编译没问题但有些不符常规的代码:

    package main func main() { for i, j := 0, 10 i < 10 j-- { if i++ i > j { break } println(i) } } 

    本文首发在微信 Go 101 公众号,欢迎各位转载本文。Go 101 公众号将尽量在每个工作日发表一篇原创短文,有意关注者请扫描下面的二维码。

    101-group-qrcode-1.jpg

    24 条回复    2019-05-20 09:59:51 +08:00
    gamexg
        1
    gamexg  
       2019-05-08 21:59:38 +08:00
    项目中这么写会被打死吧?

    ide 自动格式化代码,变成了:


    ```
    package main

    func alwaysFalse() bool {
    return false
    }

    func main() {
    switch alwaysFalse(); {
    case true:
    println("真")
    case false:
    println("假")
    }
    }

    ```
    acehow
        2
    acehow  
       2019-05-08 22:16:21 +08:00 via Android
    这种莫名其妙的写法有什么意义哦。能写出这种代码只能说明这人根本不会 go 语言。
    liulaomo
        3
    liulaomo  
    OP
       2019-05-08 22:20:41 +08:00
    @acehow 重在阐述断行规则,;)
    zhujinliang
        4
    zhujinliang  
       2019-05-08 22:26:57 +08:00 via iPhone
    感谢分享,以后碰到类似 bug 可以注意到这个问题
    AngelCriss
        5
    AngelCriss  
       2019-05-08 22:34:48 +08:00 via Android   9
    这不就是说 go 语言设计傻逼吗
    SuperMild
        6
    SuperMild  
       2019-05-08 22:44:02 +08:00
    在这个 IDE/编辑器智能提醒已经是标配的年代,这些知识已经意义不大了。
    whoami9894
        7
    whoami9894  
       2019-05-08 23:03:54 +08:00 via Android
    > switch 代码块中开大括号{前的比较表达式可以省略,其默认值为 true

    这句话的含义是?

    ```
    switch {
    case true:
    xx
    case false:
    xx
    }

    switch a{
    case 1:
    xx
    case 2:
    xx
    }
    ```

    只有第一种类似 cond 的语义可以忽略吧
    liulaomo
        8
    liulaomo  
    OP
       2019-05-08 23:11:35 +08:00
    @whoami9894

    >> switch 代码块中开大括号{前的比较表达式可以省略,其默认值为 true
    > 这句话的含义是?


    其实 switch 代码块中开大括号{前的简单语句也可以省略,因此有以下 4 种变种:

    ```
    switch aSimpleStatement; anExpression {...}
    switch aSimpleStatement; {...} // <=> switch aSimpleStatement; true {...}
    switch anExpression {...}
    switch {...} .. <=> switch true {...}
    ```
    whoami9894
        9
    whoami9894  
       2019-05-09 00:09:39 +08:00 via Android
    @liulaomo
    噢噢理解错了,现在明白你的意思了,是因为 go 分支语句的糖导致的。alwaysFalse()被当做 statement 了
    gramyang
        10
    gramyang  
       2019-05-09 07:58:27 +08:00   1
    go 语言的大括号是不能像 c++那样换行写的。因为 go 默认给每行语句添加一个;,这篇文章能解决所有问题 https://segmentfault.com/a/1190000013739000
    usingnamespace
        11
    usingnamespace  
       2019-05-09 09:20:23 +08:00 via iPhone
    @SuperMild 确实 别整些这种没用的玩意
    usingnamespace
        12
    usingnamespace  
       2019-05-09 09:20:53 +08:00 via iPhone
    @zhujinliang ?这样写 go 的代码不怕被骂死?
    liulaomo
        13
    liulaomo  
    OP
       2019-05-09 09:56:05 +08:00
    @gramyang

    > go 语言的大括号是不能像 c++那样换行写的。因为 go 默认给每行语句添加一个;

    我感觉这篇文章白写了, ;D

    本文重在阐述断行规则,;)
    => 很多人印象中的规则“代码块的开大括号不能被放在下一行”其实只是一个通常的而非普适的规则。
    Muninn
        14
    Muninn  
       2019-05-09 09:57:48 +08:00
    我觉得楼主分享的挺好的,怎么这么多人喷。
    reus
        15
    reus  
       2019-05-09 10:08:29 +08:00
    写了几年 go,没想到 switch 还能定义变量…… 虽然 switch v := v.(type) 也没少写……
    HarrisonZ
        16
    HarrisonZ  
       2019-05-09 10:19:26 +08:00
    茴香豆有几种写法~~~
    lyy16384
        17
    lyy16384  
       2019-05-09 10:30:08 +08:00
    不会 go,谁解释一下 true{case}这样的语句为什么能被当成 switch ?难道和上一行加了分号的 switch 还是同一条语句吗
    还有这种自动写一个 true 在中间的设计有什么好处
    zhujinliang
        18
    zhujinliang  
       2019-05-09 10:48:08 +08:00 via iPhone
    @usingnamespace

    昨晚没看仔细

    正常这个问题不应该发生,一般使用 if 判断布尔分支,不应出现使用 switch 对 bool 型进行分支的做法。

    使用包装过的 bool 型是否会出现问题?测试了 type Boolean bool,之后使用 Boolean 类型,编译不通过,提示 mismatched types Boolean and bool,意味着假设使用 bool 做了个只有两种情况的枚举(不建议这样做),之后 case 每种情况,也不会出现问题。但注意 type Boolean = bool 形式可以编译通过。

    如果编译报错,出现在 switch 附近,提示什么类型不匹配,可以先注意下是否多打了换行,避免傻呵呵地找类型问题
    lepig
        19
    lepig  
       2019-05-09 11:00:59 +08:00
    作为新手来学习一下
    AngryPanda
        20
    AngryPanda  
       2019-05-09 11:05:54 +08:00
    这种代码根本没办法通过 lint 检查
    TypeErrorNone
        21
    TypeErrorNone  
       2019-05-09 11:26:59 +08:00
    go fmt 格式化一下
    这么写不就是之前学 c 的时候如何计算 ++i++ 嘛
    rvtea
        22
    rvtea  
       2019-05-09 16:43:25 +08:00
    这么写确实容易被打死啊。。。
    去 playground 贴上代码然后 format 一下之后就看出端倪了,反正确实不太可能这么写吧,真这么写的估计踩着坑之后会
    luw2007
        23
    luw2007  
       2019-05-20 09:59:12 +08:00
    package main

    func alwaysFalse() bool {
    return false
    }

    func main() {
    switch alwaysFalse();{
    case true:
    println("真")
    case false:
    println("假")
    }
    }


    多了个分号,然后语义变了。 这块确实要注意。
    luw2007
        24
    luw2007  
       2019-05-20 09:59:51 +08:00
    ```golang
    package main

    func alwaysFalse() bool {
    return false
    }

    func main() {
    switch alwaysFalse();{
    case true:
    println("真")
    case false:
    println("假")
    }
    }

    ```
    多了个分号,然后语义变了。 这块确实要注意。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3278 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 11:42 PVG 19:42 LAX 03:42 JFK 06:42
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86