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

为什么我觉得 GO 有些语法很奇怪?

  •  
  •   Lighthughjiajin · 2022-05-29 10:27:10 +08:00 · 8173 次点击
    这是一个创建于 900 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我是初学者,刚开始学习 Go ,遇到了下面这些问题…有没大佬给我讲讲 Go 为什么这么设计…

    1 、为什么导入包的时候,需要使用双引号引起来,使用的时候确直接是包名(像 Java, Python 导入时不需要双引号,使用时也不需要,可以明确的知道导入对象后,给一个变量指向它)

    2 、为什么 package main 的时候又不需要双引号引起来了?

    3 、为什么定义变量的方式有这么多?

    4 、为什么在函数体外又不能使用 := 定义变量?

    5 、为什么使用 var 或者 := 定义的变量不使用,无法通过编译?但是 const 定义的变量却可以?

    59 条回复    2022-06-03 22:23:56 +08:00
    ic3z
        1
    ic3z  
       2022-05-29 10:34:03 +08:00
    别问,问就是语言特性。
    不过 golang 的定义变量方式甩 JAVA 一条街。
    liuxu
        2
    liuxu  
       2022-05-29 10:39:44 +08:00
    这就好比在问为什么胡萝卜是黄色的,白菜明明是绿色的为什么叫白菜
    pastor
        3
    pastor  
       2022-05-29 10:39:48 +08:00   ❤️ 10
    好家伙,我直呼好家伙!

    你要是先学的 go 后学的其他语言,那你的这种问题是不是会更多?
    Mohanson
        4
    Mohanson  
       2022-05-29 10:51:40 +08:00   ❤️ 2
    你的问题就好像 "猫为什么要长的和狗不一样?"
    cmdOptionKana
        5
    cmdOptionKana  
       2022-05-29 10:54:20 +08:00
    没有完美的设计,一切设计都是一种选择,都有得有失。

    举个例子 “什么定义变量的方式有这么多?” 这个问题,你想象一下,如果让你来来设计语言,你只能选择:

    1. 只提供一种定义变量的方式(优点是规则更少更清晰、更易理解,缺点是不方便)
    2. 提供多种定义变量的方式(优点是方便,缺点是规则相对稍稍复杂一点)

    你还有第三种选择吗?没有吧。

    那么,你应该能发现,你无法做到完美、面面俱到,任何一种选择,都有缺点,你只能选择一种缺点。

    顺着这个思路,你可以思考一下你的其他问题,这种选择它有什么优点,有什么缺点?(只要你愿意思考,不可能一个缺点、一个优点都找不到吧)
    lsry
        6
    lsry  
       2022-05-29 10:56:48 +08:00   ❤️ 8
    我的评价是非要标新立异,引起关注,增加使用者。
    cmdOptionKana
        7
    cmdOptionKana  
       2022-05-29 10:57:36 +08:00   ❤️ 3
    @liuxu
    @Mohanson

    植物、动物、人类语言都是生长出来的,不是被设计出来的。生长与设计,是事物形成的两种截然不同的方式,区别很大,不能类比。
    whenov
        8
    whenov  
       2022-05-29 10:59:43 +08:00 via Android   ❤️ 19
    OP 问的明明是语言内部不一致的地方,而不是与其他语言不同的地方
    none
        9
    none  
       2022-05-29 11:05:11 +08:00
    你先去学习下 Rust ,就不会觉得 Go 的语法奇怪了
    cmdOptionKana
        10
    cmdOptionKana  
       2022-05-29 11:08:39 +08:00   ❤️ 1
    @whenov 没想到是问内部不一致,因为我初看没有这个问题。

    比如 package main 与 import 一个是声明本页代码属于哪个包,一个是引用其他包,完全是不同的功能,有不同的表现就很自然,反而如果让不同的功能用相同的表现才奇怪。因此没往内部不一致的方向想。
    Lighthughjiajin
        11
    Lighthughjiajin  
    OP
       2022-05-29 11:14:00 +08:00
    @cmdOptionKana 谢谢大佬,你的回答解决了我对 1 、2 、3 的疑虑。4 、5 的疑虑我在后续的学习,看看能不能自己悟道?
    anxn
        12
    anxn  
       2022-05-29 11:14:18 +08:00
    入乡随俗
    SMGdcAt4kPPQ
        13
    SMGdcAt4kPPQ  
       2022-05-29 11:23:28 +08:00 via Android   ❤️ 1
    先去学习下 Rust ,就觉得 Go 的语法更奇怪了
    wdwwtzy
        14
    wdwwtzy  
       2022-05-29 11:26:59 +08:00 via iPhone   ❤️ 2
    就是很奇怪啊,有些地方不严谨
    cmdOptionKana
        15
    cmdOptionKana  
       2022-05-29 11:34:55 +08:00   ❤️ 11
    @Lighthughjiajin

    4. 用 := 定义变量其实相当于一种语法糖,这种语法糖的好处是方便,缺点是类型不够明显,传统的写明类型的方式的优点是类型很明显,可以减少 “不小心” 造成的错误。

    同时,Go 语言的创造者是 C 语言的老玩家了,像这样的老玩家有一个思想是:如果定义的地方与调用的地方离得远,就需要更清晰,如果定义的地方与调用的地方离得近,就可以更追求简化与方便。比如变量名,如果定义完马上就用,并且在很小范围内用,超出范围就不用了,这样的变量就可以用 i, x, y 之类的单字母,而使用范围越大的变量,就越倾向于用更长的名字。

    因此, := 限制在函数内使用就很合理了。

    5. const 不是变量,是常量。常量的用途通常是全局性的,而且数量通常也很少,并且通常会集中在一个地方定义,因此不容易出现 “因忘记使用变量而产生 bug” 的情况,而且常量还涉及较长远的设计,有时会先定义一堆常量,后续再慢慢用。

    但变量不一样,绝大多数情况下变量定义完都是马上就要用的,如果忘了用,极大可能产生 bug 。
    linglin0924
        16
    linglin0924  
       2022-05-29 11:52:41 +08:00 via Android
    周经贴,没意思了都
    surbomfla
        17
    surbomfla  
       2022-05-29 11:55:30 +08:00 via Android   ❤️ 7
    别钓了,鱼塘里全是钩子,没有鱼了
    NXzCH8fP20468ML5
        18
    NXzCH8fP20468ML5  
       2022-05-29 12:19:28 +08:00
    讲真,吐槽这些没意思,不如吐槽 interface {} 满天飞,绝对一大波人赞同你。
    dacapoday
        19
    dacapoday  
       2022-05-29 12:20:48 +08:00
    问题 1 ,go 是特意设计 import 后跟字符串的,将包导入与实现方式解耦
    adoal
        20
    adoal  
       2022-05-29 12:25:54 +08:00 via iPhone
    你标题“为什么我觉得 GO 有些语法很奇怪”,内文问的实际上是“为什么 GO 有些语法很奇怪”😄
    如果是针对你在标题里的字面提问,那回答就是因为你习惯了其它语言,暂时不适应 go 呗。大部分编程语言为了实用的考虑或者因为有历史局限总会有不少不一致的地方,只不过适应了就不觉得了。
    StevenRCE0
        21
    StevenRCE0  
       2022-05-29 12:37:46 +08:00
    @cmdOptionKana 可以可以,体会到了语言设计哲学思想
    bruce0
        22
    bruce0  
       2022-05-29 12:45:55 +08:00
    引用我高中英语老师的一句话, 语法没有为什么 就是这样规定的. 苹果为什么叫 Apple 香蕉为什么叫 banana 没有为什么 就是这样规定的
    sciel
        23
    sciel  
       2022-05-29 12:48:11 +08:00
    周经贴,下次换个新奇的角度~
    gogorush
        24
    gogorush  
       2022-05-29 12:49:21 +08:00
    https://go.dev/ref/spec
    大概看一遍 不懂就多问 也可以先自己查下为啥 手动狗头
    ClericPy
        25
    ClericPy  
       2022-05-29 13:10:36 +08:00   ❤️ 7
    虽然是周经, 但回帖似乎分成了两派: 一派觉得楼主是吐槽, 完全没有解答的欲望; 另一派认认真真回答楼主的疑问, 不管问的问题多常见

    后者是我七年前刚来 V2 时候的熟悉的味道, 前者是我二十年前逛贴吧的味道... 至少我觉得楼主问的问题和那几个回答都挺有价值的
    Trim21
        26
    Trim21  
       2022-05-29 13:14:53 +08:00 via Android   ❤️ 1
    package 和 import 这个问题,因为 import 的时候是一个 git repo ,所以可能有奇怪的字符吧…
    但是包名不能有奇怪的东西比如分割域名的.,所以不需要双引号也不会有奇怪的语法出现
    Trim21
        27
    Trim21  
       2022-05-29 13:18:22 +08:00 via Android
    @Trim21 所以我猜测一下,是方便那群人写语法解析器
    yolee599
        28
    yolee599  
       2022-05-29 13:23:29 +08:00 via Android
    我看到 go 写的 hello world 的时候就觉得这个语言的语法比较随意,如果之前是写 java ,C# 的大概率会不适应,如果之前是写 Python ,JavaScript 那就不觉得奇怪了
    sciel
        29
    sciel  
       2022-05-29 13:31:44 +08:00   ❤️ 1
    @ClericPy 说得很对,下次注意了,对提问者没有什么帮助的信息还是不要回好了。抱歉~

    我也是用 golang 的,等你用久了就可以体会,其实 golang 的语法还是很不错的。

    以一个获取接口数据的方法为例,返回状态为 0 和 1 时的数据。 用 d,err:= 方便接收又不用加新的类型定义,switch 的 case 可以同时 case 多个值。整体逻辑写起来可以很清爽。 而且想要测试方法,创建一个 TestFetchData 方法就可以。

    ```
    func fetchData2(ctx context.Context, url string) (*gjson.Json, error) {
    d, err := gclient.New().Timeout(time.Second*3).Retry(5, time.Second*3).Get(ctx, url) // 重试 5 次,每次间隔 3 秒
    if err != nil {
    glog.Error(ctx, err)
    return nil, err
    }
    json, err := gjson.DecodeToJson(d.ReadAll())
    if err != nil {
    return nil, err
    }
    switch json.Get("code").Int() {
    case 0, 1:
    return json.GetJson("data"), nil
    default:
    return nil, errors.New("获取数据失败,请稍后重试")
    }
    }
    llbbzh
        30
    llbbzh  
       2022-05-29 13:43:04 +08:00
    提供一条思路:

    1/2. 因为导入包的时候,包的路径比较复杂,有点、斜杠、短杠等各种特殊符号;而定义包名的时候,包名一定是一个标识符。如果语法上设计成包的路径用引号包裹起来的话,编译器中的 tokenizer 和 parser 就很好写。如果不是的话,就要为 import 包设计一套特殊的处理逻辑,很麻烦
    Jooooooooo
        31
    Jooooooooo  
       2022-05-29 14:02:19 +08:00
    我感觉楼主问的几个问题确实是好问题.

    有一个我能解答, 为什么这么多种定义变量的方法.

    这都是语法糖吧, 如果像 java 都写 Person p = new Person(), 总是感觉很冗余. 右边明明和左边重复了.
    lysS
        32
    lysS  
       2022-05-29 14:09:54 +08:00
    都是设计而已,这种东西纯看设计者的经验
    假如只有 var ,v2 又会多一个贴,标题是:都 21 世纪了,Go 作为高级语言为什么没有自动类型推导?
    koebehshian
        33
    koebehshian  
       2022-05-29 14:10:19 +08:00
    对于第 1 个问题,我搜了一下: https://stackoverflow.com/questions/36666112/why-go-requires-double-quoted-import-declaration
    就是因为是路径,不是包名,路径中可能需要转义。
    这样第 2 个问题,也很好解释了,就是只是包名,不是路径,就不需要引号
    Suddoo
        34
    Suddoo  
       2022-05-29 14:25:34 +08:00
    问就是大道至简
    ClericPy
        35
    ClericPy  
       2022-05-29 14:36:42 +08:00
    @sciel 没有针对你哈...

    就是感觉楼主新手上路时候, 怀有好奇心探索精神对一些设计想探究一下原因, 实际上并不是上面很多人嘴里的吐槽语法或者和其他语言比孰强孰弱. golang 作为一个较新的语言, 反传统语言的一些设计肯定是有原因的, 探讨一下是非常有好处的. 至于我也没说什么和问题有关的, 实在是已经被人回答的挺好了(有几个问题也解开了我的疑惑)
    a90120411
        36
    a90120411  
       2022-05-29 14:38:07 +08:00   ❤️ 4
    结论:Go 从语法层面本身就谈不上优雅。

    以下均为个人主观观点,能力有限,如有错误,请大家不吝斧正:

    #1 & #2:package 定义的是 标识符(有名称字符限制,参考 3 ),import 定义的是包所在路径。import 加了引号容易解析(路径中可能包含路径分隔符,空格、点符号等),此外保留了一些灵活性,联想一些代码生成器之类的吧。
    但其实 import 的引号是完全可以去除或者做成可选的,即使它包含特殊符号的情况下,并不是很难的解析。

    参考:
    [1] package declaration without quotes while import package use quotes ?
    https://github.com/golang/go/issues/32185
    [2] https://stackoverflow.com/questions/36666112/why-go-requires-double-quoted-import-declaration
    [3] https://go.dev/ref/spec#Qualified_identifiers

    ---------

    #3:确实挺多,用起来很灵活,有些设计确实很实用,有些又感觉是开倒车。
    没有代码规范的约束,很容易产生阅读理解困难,我不是很喜欢这种类型后置的语法。

    参考:
    [1] 我们真的需要 var 吗?
    https://groups.google.com/g/golang-nuts/c/qTZemuGDV6o/m/IyCwXPJsUFIJ
    [2] 对 Go 语言的综合评价 - 王垠
    https://www.yinwang.org/blog-cn/2014/04/18/golang

    ---------

    #4:引用 Ian Lance Taylor 的回答。他是 Google 的员工,Go 的开发者。
    在全局作用域,所有的声明都要使用关键字,目的是方便解析器解析(不是很有说服力)。

    参考:
    [1] https://groups.google.com/g/golang-nuts/c/qTZemuGDV6o/m/IyCwXPJsUFIJ
    [2] https://groups.google.com/g/golang-nuts/c/OqYL9lgsPQ4/m/udp0nPHPZTgJ

    ---------

    #5:是一种刻板且体验极差的设计,但官方跟你说(参考 1 )这是为了你好。

    参考:
    [1] https://go.dev/doc/faq#unused_variables_and_imports
    [2] https://stackoverflow.com/questions/21743841/how-to-avoid-annoying-error-declared-and-not-used
    acehowxx
        37
    acehowxx  
       2022-05-29 16:40:21 +08:00 via Android
    对于 go .除了 if err!=nil 以外,对其他方面都比较满意。未来的错误处理如果用?向上抛的话可以省掉大量的 err 判断。
    Zwying
        38
    Zwying  
       2022-05-29 19:57:12 +08:00
    倘若你一开始学习的就是 Go ,你或许会对 java 发出一样的感叹,习惯而已
    alsas
        39
    alsas  
       2022-05-29 20:11:53 +08:00
    问就是特立独行 问就是工程设计
    c8iter
        40
    c8iter  
       2022-05-29 20:26:47 +08:00
    多线程中断管理,真是酸爽
    monetto
        41
    monetto  
       2022-05-29 20:37:24 +08:00
    @pastor C/C++/Java 他们三个的语法大体是很像的。三者加起来市场占比也是最高的。Go 的语法个人感觉像是 C + Python 结合体。
    gamexg
        42
    gamexg  
       2022-05-29 20:38:15 +08:00
    第一点,导入路径用引号。
    我以前学某语言的时候,不记得是什么语言了,也是导入,但是导入库不需要引号。
    当时就在想,这里不灵活了,不能动态生成导入路径。

    嗯,这么看,应该是 python
    印象 python 又有个专门的函数来实现变量作为导入路径。

    不过其实也就是一个选择而已,
    而且 go 时便已级别的,目前没看到有需要像 python 一样动态导入库的需求。


    我比较赞同楼上说的,为了解决路径的转义问题。
    直接用字符串,那么可以直接用现成的字符串转义表标准,不用再重新设计了。
    AItsuki
        43
    AItsuki  
       2022-05-29 20:41:20 +08:00
    问的很好,但大多都是语言设计的问题。有些写法单纯就是为了编译器好解析,偷懒了,最典型的偷懒例子就是 go 不支持函数重载。
    gogogo1203
        44
    gogogo1203  
       2022-05-29 20:41:32 +08:00
    @acehowxx 出错了就包含这一层的 err ,往外抛,一直丢到该处理错误的那一层。这个方法放到哪里都是没有错的。再写个处理 err 的中间件,没毛病
    Lighthughjiajin
        45
    Lighthughjiajin  
    OP
       2022-05-29 20:42:25 +08:00
    @gamexg 你指的应该是 python 的 importlib
    gogogo1203
        46
    gogogo1203  
       2022-05-29 20:43:15 +08:00
    每个星期都有一些自称“刚入门“go 的人开贴 diss 一下 go 的各种不足。有没有一种可能,他们是 100%对的呢?
    Lighthughjiajin
        47
    Lighthughjiajin  
    OP
       2022-05-29 20:44:36 +08:00
    谢谢大佬们的回答~
    我平时主要写 Python ,偶尔写 JS 。
    这次想学习 Go 是因为听说 Go 生成可执行文件方便,并且不好反编译(也就是保护源码)。
    刚开始接触,遇到的这些情况,只是自己的思考,并没有批判的意思。
    ZE3kr
        48
    ZE3kr  
       2022-05-29 20:49:46 +08:00 via iPhone
    很明显楼主最先接触的是 C 或是类 C 的语言

    楼主可以看看 SML/NJ 、Ruby 、Lisp ,这些编程语言更奇怪(但也都非常有意思)
    jiangzm
        49
    jiangzm  
       2022-05-29 20:52:03 +08:00   ❤️ 1
    我的感觉 go 的语法是想出奇推新, 为了不一样而不一样, 你底层怎么设计无所谓,但是语法层面没必要一定要和类 C 语言不一样
    iosyyy
        50
    iosyyy  
       2022-05-29 22:42:13 +08:00
    @koebehshian 就是给偷懒找个接口 java 的 import 也不是有特殊字符吗..也是不很难解析啊
    maigebaoer
        51
    maigebaoer  
       2022-05-29 23:07:12 +08:00 via Android   ❤️ 1
    go 团队说习惯了就不会奇怪
    ragnaroks
        52
    ragnaroks  
       2022-05-30 00:24:13 +08:00   ❤️ 1
    用现在的话来说叫做粉丝提纯
    pastor
        53
    pastor  
       2022-05-30 10:59:52 +08:00
    @monetto c++的语法语义复杂度已经是编程语言天花板了,应该改名叫 Mediterranean Lang 。。。
    RickyC
        54
    RickyC  
       2022-05-30 12:28:40 +08:00
    你这种写作格式很值得肯定, 分行分段, 自带行号.
    koujianshusheng
        55
    koujianshusheng  
       2022-05-30 13:41:14 +08:00
    语法不奇怪如何对的起新兴编程语言呢
    zhanlanhuizhang
        56
    zhanlanhuizhang  
       2022-05-30 13:51:32 +08:00
    语法就是定义。创始者,觉得这样方便。
    chrisia
        57
    chrisia  
       2022-06-01 16:44:17 +08:00
    我的评价是 go 简约但不优雅,另一个极端是 kotlin 优雅但不简约,java 是既不优雅也不简约
    Lighthughjiajin
        58
    Lighthughjiajin  
    OP
       2022-06-03 22:18:30 +08:00
    追加新问题
    在我开始接触 Go 之前,有许多朋友跟我说,
    1 、Go 的语法严格(例如 { 不能单独换行,结构体里的最后一项要有 , )
    2 、Go 写的代码具有更好的可维护性 (和 Python 对比)

    最近刚学到 struct ,感觉 struct 和 方法 分离的方式,不太利于代码维护,很可能会出现这个文件里定义了 struct ,另一个文件里定义了 方法,那读代码的时候就很麻烦。 虽然这种情况是认为造成的,但是为什么不设计的 struct 数据 和 方法在一起。 还是说 Go 这么设计有什么原因,或者其他有益的地方?
    Lighthughjiajin
        59
    Lighthughjiajin  
    OP
       2022-06-03 22:23:56 +08:00
    为什么解引用需要加括号?是因为 * 的优先级比 . 低 吗?
    ```
    (*person).name. // <=> person.name
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1016 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 21:50 · PVG 05:50 · LAX 13:50 · JFK 16:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.