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 · 4653 次点击
    这是一个创建于 2017 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    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   ·   实用小工具   ·   1119 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 22:48 · PVG 06:48 · LAX 14:48 · JFK 17:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.