Golang Go语言中为什么我觉得有些语法很奇怪?

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

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

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

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

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

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


Golang Go语言中为什么我觉得有些语法很奇怪?

更多关于Golang Go语言中为什么我觉得有些语法很奇怪?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

60 回复

别问,问就是语言特性。
不过 golang 的定义变量方式甩 JAVA 一条街。

更多关于Golang Go语言中为什么我觉得有些语法很奇怪?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这就好比在问为什么胡萝卜是黄色的,白菜明明是绿色的为什么叫白菜

好家伙,我直呼好家伙!

你要是先学的 go 后学的其他语言,那你的这种问题是不是会更多?

你的问题就好像 “猫为什么要长的和狗不一样?”

没有完美的设计,一切设计都是一种选择,都有得有失。

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

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

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

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

顺着这个思路,你可以思考一下你的其他问题,这种选择它有什么优点,有什么缺点?(只要你愿意思考,不可能一个缺点、一个优点都找不到吧)

我的评价是非要标新立异,引起关注,增加使用者。




植物、动物、人类语言都是生长出来的,不是被设计出来的。生长与设计,是事物形成的两种截然不同的方式,区别很大,不能类比。

OP 问的明明是语言内部不一致的地方,而不是与其他语言不同的地方

你先去学习下 Rust ,就不会觉得 Go 的语法奇怪了

没想到是问内部不一致,因为我初看没有这个问题。

比如 package main 与 import 一个是声明本页代码属于哪个包,一个是引用其他包,完全是不同的功能,有不同的表现就很自然,反而如果让不同的功能用相同的表现才奇怪。因此没往内部不一致的方向想。

谢谢大佬,你的回答解决了我对 1 、2 、3 的疑虑。4 、5 的疑虑我在后续的学习,看看能不能自己悟道?

入乡随俗

先去学习下 Rust ,就觉得 Go 的语法更奇怪了

就是很奇怪啊,有些地方不严谨



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

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

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

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

但变量不一样,绝大多数情况下变量定义完都是马上就要用的,如果忘了用,极大可能产生 bug 。

周经贴,没意思了都

别钓了,鱼塘里全是钩子,没有鱼了

讲真,吐槽这些没意思,不如吐槽 interface {} 满天飞,绝对一大波人赞同你。

问题 1 ,go 是特意设计 import 后跟字符串的,将包导入与实现方式解耦

你标题“为什么我觉得 GO 有些语法很奇怪”,内文问的实际上是“为什么 GO 有些语法很奇怪”😄
如果是针对你在标题里的字面提问,那回答就是因为你习惯了其它语言,暂时不适应 go 呗。大部分编程语言为了实用的考虑或者因为有历史局限总会有不少不一致的地方,只不过适应了就不觉得了。

可以可以,体会到了语言设计哲学思想

引用我高中英语老师的一句话, 语法没有为什么 就是这样规定的. 苹果为什么叫 Apple 香蕉为什么叫 banana 没有为什么 就是这样规定的

周经贴,下次换个新奇的角度~

https://go.dev/ref/spec
大概看一遍 不懂就多问 也可以先自己查下为啥 手动狗头

虽然是周经, 但回帖似乎分成了两派: 一派觉得楼主是吐槽, 完全没有解答的欲望; 另一派认认真真回答楼主的疑问, 不管问的问题多常见

后者是我七年前刚来 V2 时候的熟悉的味道, 前者是我二十年前逛贴吧的味道… 至少我觉得楼主问的问题和那几个回答都挺有价值的

package 和 import 这个问题,因为 import 的时候是一个 git repo ,所以可能有奇怪的字符吧…
但是包名不能有奇怪的东西比如分割域名的.,所以不需要双引号也不会有奇怪的语法出现

所以我猜测一下,是方便那群人写语法解析器

我看到 go 写的 hello world 的时候就觉得这个语言的语法比较随意,如果之前是写 java ,C# 的大概率会不适应,如果之前是写 Python ,JavaScript 那就不觉得奇怪了

说得很对,下次注意了,对提问者没有什么帮助的信息还是不要回好了。抱歉~

我也是用 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(“获取数据失败,请稍后重试”)
}
}

提供一条思路:

1/2. 因为导入包的时候,包的路径比较复杂,有点、斜杠、短杠等各种特殊符号;而定义包名的时候,包名一定是一个标识符。如果语法上设计成包的路径用引号包裹起来的话,编译器中的 tokenizer 和 parser 就很好写。如果不是的话,就要为 import 包设计一套特殊的处理逻辑,很麻烦

我感觉楼主问的几个问题确实是好问题.

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

这都是语法糖吧, 如果像 java 都写 Person p = new Person(), 总是感觉很冗余. 右边明明和左边重复了.

都是设计而已,这种东西纯看设计者的经验
假如只有 var ,v2 又会多一个贴,标题是:都 21 世纪了,Go 作为高级语言为什么没有自动类型推导?

对于第 1 个问题,我搜了一下: https://stackoverflow.com/questions/36666112/why-go-requires-double-quoted-import-declaration
就是因为是路径,不是包名,路径中可能需要转义。
这样第 2 个问题,也很好解释了,就是只是包名,不是路径,就不需要引号

问就是大道至简

没有针对你哈…

就是感觉楼主新手上路时候, 怀有好奇心探索精神对一些设计想探究一下原因, 实际上并不是上面很多人嘴里的吐槽语法或者和其他语言比孰强孰弱. golang 作为一个较新的语言, 反传统语言的一些设计肯定是有原因的, 探讨一下是非常有好处的. 至于我也没说什么和问题有关的, 实在是已经被人回答的挺好了(有几个问题也解开了我的疑惑)

结论: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

对于 go .除了 if err!=nil 以外,对其他方面都比较满意。未来的错误处理如果用?向上抛的话可以省掉大量的 err 判断。

倘若你一开始学习的就是 Go ,你或许会对 java 发出一样的感叹,习惯而已

问就是特立独行 问就是工程设计

多线程中断管理,真是酸爽

C/C++/Java 他们三个的语法大体是很像的。三者加起来市场占比也是最高的。Go 的语法个人感觉像是 C + Python 结合体。

第一点,导入路径用引号。
我以前学某语言的时候,不记得是什么语言了,也是导入,但是导入库不需要引号。
当时就在想,这里不灵活了,不能动态生成导入路径。

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

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


我比较赞同楼上说的,为了解决路径的转义问题。
直接用字符串,那么可以直接用现成的字符串转义表标准,不用再重新设计了。

问的很好,但大多都是语言设计的问题。有些写法单纯就是为了编译器好解析,偷懒了,最典型的偷懒例子就是 go 不支持函数重载。

出错了就包含这一层的 err ,往外抛,一直丢到该处理错误的那一层。这个方法放到哪里都是没有错的。再写个处理 err 的中间件,没毛病

你指的应该是 python 的 importlib

每个星期都有一些自称“刚入门“go 的人开贴 diss 一下 go 的各种不足。有没有一种可能,他们是 100%对的呢?

谢谢大佬们的回答~
我平时主要写 Python ,偶尔写 JS 。
这次想学习 Go 是因为听说 Go 生成可执行文件方便,并且不好反编译(也就是保护源码)。
刚开始接触,遇到的这些情况,只是自己的思考,并没有批判的意思。

很明显楼主最先接触的是 C 或是类 C 的语言

楼主可以看看 SML/NJ 、Ruby 、Lisp ,这些编程语言更奇怪(但也都非常有意思)

我的感觉 go 的语法是想出奇推新, 为了不一样而不一样, 你底层怎么设计无所谓,但是语法层面没必要一定要和类 C 语言不一样

就是给偷懒找个接口 java 的 import 也不是有特殊字符吗…也是不很难解析啊

go 团队说习惯了就不会奇怪

用现在的话来说叫做粉丝提纯

c++的语法语义复杂度已经是编程语言天花板了,应该改名叫 Mediterranean Lang 。。。

你这种写作格式很值得肯定, 分行分段, 自带行号.

语法不奇怪如何对的起新兴编程语言呢

语法就是定义。创始者,觉得这样方便。

我的评价是 go 简约但不优雅,另一个极端是 kotlin 优雅但不简约,java 是既不优雅也不简约

追加新问题
在我开始接触 Go 之前,有许多朋友跟我说,
1 、Go 的语法严格(例如 { 不能单独换行,结构体里的最后一项要有 , )
2 、Go 写的代码具有更好的可维护性 (和 Python 对比)

最近刚学到 struct ,感觉 struct 和 方法 分离的方式,不太利于代码维护,很可能会出现这个文件里定义了 struct ,另一个文件里定义了 方法,那读代码的时候就很麻烦。 虽然这种情况是认为造成的,但是为什么不设计的 struct 数据 和 方法在一起。 还是说 Go 这么设计有什么原因,或者其他有益的地方?

为什么解引用需要加括号?是因为 * 的优先级比 . 低 吗?
<br>(*person).name. // &lt;=&gt; <a target="_blank" href="http://person.name" rel="nofollow noopener">person.name</a><br>

在Golang(又称Go语言)中,你可能会觉得某些语法看起来与其他编程语言不同,甚至显得有些“奇怪”。这主要是因为Go语言的设计哲学强调简洁、清晰和一致性,旨在减少复杂性并提高代码的可读性。

例如,Go语言的函数声明和调用方式可能与你之前使用的语言有所不同。它采用简洁的函数签名,明确区分了接收者和参数,这有助于快速理解函数的用途。此外,Go语言还引入了多返回值的概念,这在某些场景下可以更加直观地处理错误和结果。

另一个可能让你觉得“奇怪”的方面是Go语言的并发处理。它内置的goroutine和channel机制,虽然初看之下可能不太直观,但实际上提供了一种高效且简洁的并发编程方式。这种设计鼓励程序员以更简洁、更自然的方式编写并发代码,而不是依赖于复杂的线程同步机制。

当然,任何新语言的学习都需要一个适应过程。Go语言的这些“奇怪”语法,实际上是其独特设计理念的体现。随着你对Go语言的深入了解和实践,相信你会逐渐发现这些语法的合理性和实用性。

总之,Go语言的语法设计是经过深思熟虑的,旨在提高代码的可读性和可维护性。如果你在使用过程中遇到困惑或不解,不妨多查阅官方文档和社区资源,相信你会逐渐掌握这门强大的编程语言。

回到顶部