谁是虽好的语言 ?- 语言选型闲聊(下)

652 阅读9分钟

大家好,我是肖恩,源码解析每周见

近期我们公司做架构升级,调研了一下各种语言, 包括TypeScript,c#,rust, 还有java和go。这个过程中有一些个人看法,可能会有些偏颇或者不正确的地方,我就简单一说,大家一乐,无意引战。

在上篇中,我简单介绍了一下游戏行业的一些技术栈特点,以及python,Typescript和c#三种语言的使用感受,没有看过的同学可以去看看链接。本文继续介绍go和java,rust的故事。

go 语言

Go 是一种开源编程语言,可以轻松构建 简单、可靠且高效的软件。Go 语言诞生于 2009 年,由google开源,发展到今天已经有过去了10 多年。伴随着云原生的发展,Go语言也是轰轰烈烈红红火火;许多知名的框架,docker,Kubernetes,etcd等都是使用go实现。国内最近的新闻是字节这样的大厂全面拥抱,很多培训机构也在推各种go课程,一切看着都很不错。

我们先简单感受一下,编写下面的的main.go:

package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}

直接运行这个hello程序:

# go run main.go
Hello, 世界

分别使用下面3个命令,将hello编译成对应操作系统的可执行程序:

GOOS=darwin GOARCH=amd64 go build -o hello_mac
GOOS=linux GOARCH=amd64 go build
GOOS=windows GOARCH=amd64 go build

查看各自的编译结果:

➜  file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=YPrL0x5YX4DVXlh5kkKE/Y2HJXrRQkDsOpEXTVASf/NrLBfe3AB23u7MFL1ITI/GpLRUEatK4FJEmJ8Q5mW, not stripped
➜  file hello.exe
hello.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
➜  file hello_mac
hello_mac: Mach-O 64-bit executable x86_64

在mac下可以直接执行hello_mac:

➜  hello ./hello
zsh: exec format error: ./hello
➜  hello ./hello.exe
zsh: exec format error: ./hello.exe
➜  hello ./hello_mac
Hello, 世界

这种不需要虚拟机/解释器环境,可以直接执行的能力非常强大。

我自己接触go大概是14年的样子,那时候大概看的是beego的作者Asta谢的书吧。可惜在当时看来感觉go是一个很语法诡异的语言,还不如php/java省事。第二次接触是19年,因为要临时支援一个项目,同时又因为对docker,k8s的学习,了解了go的社区发展,觉得这会是一个很有前景的语言。奈何后来项目变动,又没有继续深入下去。最近算是第三次接触吧,先是写了一些小工具和小服务,计划会写一点网关服务,应该有机会深入下去。无独有偶,发现也有和我感觉类似的同学,我直接摘录在下面:

from draveness

作者目前也使用 Go 语言作为日常开发的主要语言,虽然 Go 语言没有 Lisp 系语言的开发效率和强大表达能力,但是却是一门非常容易使用并且大规模运用的工程语言,这也是作者学习和使用 Go 语言的主要原因。

作者是从 2018 年才开始学习和使用 Go 语言的,刚刚接触 Go 语言时是有些排斥和拒绝的,一度认为 Go 语言 GOPATH 的设计非常诡异,而简单的语法也导致了低下的表达能力并且影响开发效率。但是随着对 Go 语言的深入学习和理解,作者的这一观念也在不断改变。

到了今天,作者认为我们在工业界需要这么一门语法简单的编译型语言,它能够提供简单的抽象和概念,虽然目前 Go 语言也有很多问题,但是语言以及周边工具的不断完善也让作者感受到了社区的活力,也坚定地认为这门语言未来的发展会越来愈好。

学go时候需要一定的适应期。比如下面一段代码:

# gin/context.go

func (c *Context) GetString(key string) (s string) {
	if val, ok := c.Get(key); ok && val != nil {
		s, _ = val.(string)
	}
	return
}

代码理解起来不难,从一个context中取一个key的值后转换成字符串返回。不同的地方在:

  • if val, ok := c.Get(key); ok && val != nil {...} 这样的三段式结构,这种语法几乎遍布在go的任何函数。这是因为go没有try/catch语法,所有的函数都要做异常判断。
  • (c *Context) 这样的接口实现方式,和面向对象的接口实现有很大的差别。

初期看go的代码,会感觉非常别扭,渡过适应期后,就觉得这种实现方式也没什么问题。然后再陷入go并发不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存的坑中。

go的学习过程,大概就像是windows电脑使用简单还是mac电脑使用简单的争论,是一个打破先入为主印象的问题。

java和rust

java不用介绍了,工业界的霸主,大厂的最爱。优点非常多,缺点嘛,主要就是个人不精通,已经放弃。

Rust是一门赋予每个人构建可靠且高效软件能力的语言。Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务。Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。看着是真棒。

我对rust的了解只是浅尝辄止,因为大家都说它上手难度极高。其中所有权的概念,是其核心功能也是核心阻碍。简单举例一下:

let s1 = String::from("hello");
let s2 = s1;

println!("{}, world!", s1);

类似上面代码在其它语言中都可以正常运行,就是把s1赋值给s2,然后打印s1。不管是浅拷贝还是深拷贝,只会涉及性能问题,不会执行错误。但是在rust中却是无法执行成功的,因为rust认为s1赋值s2后,s1不再使用了,应该被销毁,这大概就是所有权的概念。

语言生态

语言的学习,前面介绍的内容,都是语法层面。一般来说还涉及语言的生态或者社区,我自己简单归纳语言生态包括下面几个部分:

语言生态

学习语言的语法后,需要了解语言如何执行。比如python,就需要了解cpython的解释器,了解GIL,了解GC;java需要做JVM的调优,要熟练掌握某种编程语言,仅仅会语法显然是不够的。不同的语言,一般还有不同的IDE工具,比如c#大家惯用的是Visual Studio,熟练使用IDE工具,才可以提高研发效率。虽然现在VSCode,也还不错,但是如果不安装插件,不太好用,安装插件,对新手又不够友好。最好的办法,还是使用jetbrains家的工具,pycharm,rider,goland都是神器。我们还需要学习语言的包管理工具,这样才可以利用于同语言的各种库,站在开源的肩膀上。

语言的社区也是非常重要的一环。首先就是文档,是否有足量的文档去介绍各种语法细节,并且是否有中文文档,对语言学习入门有很大的帮助。我个人比较习惯,中英文文档对照着看,中文看的快,对一些感觉中文介绍不太顺的地方,再从英文中对比理解。然后就是是否有足够多的开源项目,可以去使用,有杀手级的应用可以去参考。开源项目够足,编程实现的时候才会便捷,一般工作都是搬砖,不用从沙子开始造起。

学习语言也是为了工作,除了语言自身的情况,还要考虑一下招聘市场和人才市场。招聘市场可以从岗位的数量和薪资情况反映,下面是一个语言及工资情况的统计数据:

注意: 贴图仅仅示意,并不代表真实的数据

人才市场可以从语言的流行度反映, 比如TIOBE2019-2020年的语言流行度排名:

从上图看Java和Python都还不赖。我个人把各种语言总结成下表:

语言语法环境包管理社区适合领域个人推荐
python简单cpython/pypypip丰富应用广泛:爬虫,数据分析,后端服务,AI等⭐️⭐️⭐️⭐️
typescript简单node&&v8npm丰富推荐前端应用,或者基于前端的全栈⭐️⭐️⭐️
c#中等dotnetnuget丰富游戏制作⭐️⭐️⭐️
go特别-go-mod丰富云原生服务⭐️⭐️⭐️⭐️
java中等jvmmaven丰富anywhere⭐️⭐️⭐️
rust特别-crates一般高性能网络服务,WebAssembly,嵌入式⭐️⭐️⭐️⭐️

在学习和了解各种语言的语法,对自己的编程能力提升有帮助,可以开阔不少视野。大家关注不同语言应该重点放在这一块, 对于语言的生态,除非打算用来工作,否则不建议深入,因为不用就会忘记,简单了解即可。带着思考去学习不同语法,比如下面一个python函数:

def readme(a, b):
    with open('README.rst', 'w') as f:
        c = 1
        a = a+c;
        b.append("b2")
        f.write(a)
        f.write(b)
    print(a, c)
    return b

我们可以思考下面几个问题:

  • 参数a和b的类型
  • 入参和出参
  • 参数的值传递和引用传递
  • b的值和引用
  • f,c,a的作用域
  • 函数是否有返回值,可以返回几个值
  • 如果函数是一个类/接口的实现,语法上又应该如何表示?

上面这几个问题,在不同的语言上,都有不同的实现或者表达方式,多看看就会更加理解函数的本质。

最后的最后,还是推荐大家学习一下c,毕竟这个是鼻祖,是历史,其它语言都是从这个根上派生而来。

参考链接