回
@Irons Du, 不是为了反对他的答案,是因为基于这些问题,能解释 Go 不受待见的其中一个原因:Go 面对的问题和整个解决思路跟 Google 软件的规模相关,而这个规模是罕见的。20 亿行源码放在一个统一的代码库,全部主干提交,理论上代码库里任何两点都可以互相调用,几万个工程师(我是其中一名猪队友)在全球各个时区协同。
Why Google Stores Billions of Lines of Code in a Single Repository. 这个设计前提导致了同样被黑得很惨的 C++ Style Guide (
The Philosophy of Google's C++ Code) 和 Go 的一些看着很奇葩的设计点。
另外这个软件规模是一个问题,不是什么值得炫耀的东西。Go 为解决这些问题的设计,并不一定适合其他场景。回答这个问题本身,这里也不是说 Go 有多好,只是可以分享一些「为什么」,很有趣的设计权衡,例如 Quora 原文提到的 Go 几乎忽略了所有现代 PL 研究成果,但实际上这些成果在这个规模上还没能很好地工作。关于 Go 为什么是(或不是)系统编程语言的问题,我可以另外写一篇。
1:你能轻松知道哪些struct继承(实现)了哪些interface么?
能,Go guru.
2016-talks/slides.pdf at master · gophercon/2016-talks · GitHub在超大软件库上一样很顺。显式 implements 声明上规模后会有问题,多余的依赖关系是其中之一,下面有关于依赖的更详细讨论。
2:你能轻松知道struct有哪些"成员函数"么?
能,godoc 啊
这两点正好说明了 Go 极端重视工具的设计思路,工具能解决大代码库上源代码级别无法解决的问题,比如全代码库索引、重构,计算改动影响范围触发集成测试等等。这里面也必须有权衡,例如,要为这个规模写编译器和各种工具,你最好别搞复杂的类型系统,不然事情会很困难。
3:手动维护defer能比RAII轻松?
RAII 很难。C++ destructor + exception, 这里的 exception 包括处理 destructor 的异常安全和 destructor 自己真的需要抛异常的情况。还有如果在 destructor 里放了重型操作,比如 flush 硬盘,defer 至少让你清楚地看到这种重操作会在哪里跑。这些问题当然都可以用很仔细的设计避免,但是在有几万个猪队友的时候,不要指望每个人都能做出好设计。
4:package只有一个层次
如果是指不能只 import 一个父节点而要显式 import 所有叶子节点。这是用来控制 dependency 的,不必要的 dependency 在大软件库是个严重问题。Go 奇葩的 import 多余 package 直接编译错误的规则也是这个目的。
5:访问控制只能限定在package之外。
个人体验,它省掉了很多语法规则,还工作得很好。有点不方便的是你看它 call 一个私有函数,但是在同一个文件里是找不到这个函数的定义的,它可能在同一个 package 的另一个文件里。这个是用工具补足的 —— 在内部的 code search 工具里我没感觉不方便,在 github 没有交叉引用的情况下看代码就比较郁闷。
6:基于源代码的开发(复用),这是否违背了以前书上说的实现隐藏(只暴露接口)?
没,主要是因为 Google 统一代码库,Go 一开始压根没考虑二进制库发布的问题。这跟软件工程的隐藏实现是两回事。依赖版本管理问题同理,因为统一代码库+全主干提交,这个问题在 Google 是不存在的…… 当然问题就是问题,现在外部使用越来越多他们也在逐步补锅了。
7:推崇error作为返回值是不对的。另外(panic+recover)对比下C++在C之上添加的异常处理(+RAII)的类型安全
推荐一篇微软 Midori 项目 (Rethinking the software stack) 语言 leader 的
Joe Duffy - The Error Model(超长)。error 功能不够好,但 C++ 和 Java 的 exception 机制在上规模后也有无法解决的工程和性能的问题,Optional是好,但是语言就要变复杂,这里面有 tradeoff. 另外,「异常安全」是个看起来遵守规则写就可以的简单事情,但实际上非常困难,比如事务的回滚,文中也有专门描述。
本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度,google,bing,sogou 等
© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有