vlambda博客
学习文章列表

边缘设备、系统及计算杂谈(2)——go语言

缘起

本期继续讨论边缘领域里的另一个有意思的东西——语言。正如前文所说,边缘设备是在终端、云端(或者叫后台系统)中出现的一种新形态的设备。它肯定不是终端,因为它比终端能力强,而且倾向于它是为终端服务的。它肯定也不是后台系统,因为边缘设备往往不会部署很多。或者,往往将所有业务系统/组件全部装在一台边缘设备上——这和后台系统里的微服务化是不太一样的。主要是不想把后台系统那套庞大、复杂的东西再搬到边缘侧了。


简单来说,用一句话来表示边缘设备上的系统开发的话,就是我们需要在边缘侧开发服务类程序。显然,java不太合适。java一个是资源消耗大,另外一个是中间件太多。这是我最初的想法,后来发现某些老外也是这么想的,比如下图的某个项目里的说法:


所以,设备端人才如果要往边缘计算领域发展的话,go语言我觉得是需要掌握的。除此之外,后台系统、云端那套微服务、分布式也是可以学的。可以说,边缘系统给众多在血海里拼搏的设备端人才开拓了一个新的疆场。BTW,不用担心后台系统的人杀向边缘侧。因为边缘侧很难玩转集群、而且都需要自己动手——我意思是绝对不是简单的写写业务逻辑就行的事情。


说说go语言

go语言是一门比较奇特的语言。我最近大概花了2-3周看了下。以前在搞以太坊钱包的时候看过一点,但这次因为实际操作了下感觉更深刻。这篇不是讲go语言特点的。今天只讲一些认识和体会,以后有机会再接着讲。边缘侧是很多技术领域的汇聚点,比如什么联邦学习,分布式AI等等。


回到go来,说两个go语言里我的感受,非技术讨论请勿上纲上线😆


go中的接口和实现

go语言中接口的定义和实现非常有想法(但好像和ObjectC挺像的)。在go语言里,接口仅定义行为——也就是函数那么谁来实现接口呢?奇怪的地方来了,go语言中没有显示实现接口的地方。通过一个代码来讲解会好一点:

边缘设备、系统及计算杂谈(2)——go语言

上面代码示例中

  • 第7到第10行定义了一个接口。注意,go语言里,类型是写在右边的。可以看到,interface只能定义行为——也就是函数

  • 第11行到第13行定义了一个struct。go语言里,struct里只有数据,没有行为。

那么,怎么把数据和行为绑定到一起呢——也就是如何实现一个接口呢?通过第15行的函数定义可以看到。第15行和第18行是go里的函数定义,但在func和函数名之间增加了一个叫receiver的部分。这个部分就是将行为和数据绑定的地方——也就是实现接口的方式。这种奇怪的接口实现方式,我理解更多是强迫开发者换一种思考角度。


当然,我目前还没有适应这种方式。从java/c++程序员转换过来的话,还是要花一点时间的。先抛开go语言设计者所想到的这种设计思路的优点,对普通开发者而言,很难适应的一个原因是我不知道哪些struct实现了哪些接口!!!虽然go语言在编译的时候会把这些信息固化(go语言是静态编译语言,这些信息编译的时候都能算出来),但对程序员而言,我还得看定义了哪些函数才知道......


接着说。上图中,第22到第27行是数据绑定接口的另外一种方式。对比第15到第20行,我们发现只不过receiver不太一样。15-20行的receiver是指针,22-27行的receiver是值。


恩,这又是go里实现接口和其它语言不太一样的地方。通过指针定义的receiver,接口函数里是可以改变receiver内部值的。而通过value定义的receiver,接口函数里无法改变receiver内部值——就是传值和传引用的区别,这个不多说了。我们看下一段有意思的地方:

边缘设备、系统及计算杂谈(2)——go语言

30行定义了x结构体变量。第31行将x赋值给interfaceX变量。32行针对这个interfaceX调用它的api1函数。结果编译错误——原因是上图中我们定义的receiver是指针类型。而interfaceX不是指针。OK,可以“理解”。接着看下一个代码:

边缘设备、系统及计算杂谈(2)——go语言

上图中,23-28行我们将receiver改成了值引用。再看test函数:

  • 第31行定义了一个x变量。

  • 第32行interfaceX取得是x的指针。

  • 第33行interfaceY取得是x的值

  • 34、35行调用api1和api2。比较奇葩的是,interfaceX明明是x的指针,它居然可以调值引用定义的api1。

  • 值类型的接口调用,api1、api2调用完后,Name都不会变。这就是值和引用(或叫指针也行)的区别。这在其他语言里都是这个表现。不多说了。

但结合前面一个图,现在你一定糊涂了,go语言里receiver类型的影响:

  • receiver*指针类型定义的接口,只能通过指针类型变量的调用,否则编译错误

  • receiver值类型定义的接口,值变量或者指针变量都可以调用!


这是为毛呢?看了好多材料,我感觉都没说清楚。我想了一种比较简单的答案(简单,往往意味着可能是对的):

  • receiver值类型定义的接口,值变量调用肯定没问题。指针变量调用也可以没问题,因为编译器会先将指针变量解引用到一个临时变量,再用这个临时变量去调用。因为receiver值类型定义的接口无法修改原参数的内容,所以相当于编译器针对指针类型变量做了一个对程序员来说少写写行代码的处理,而且不会有啥问题——因为使用receiver值类型,本身从语义上就已经约定好不能修改原参数。


这就是go语言里,interface实现时一个比较“难”的问题的解释。


interface是指针还是非指针?

还有一个问题牵扯到另外一个比较有意思的东西。看下图

第27行定义了一个x变量,它的类型是interface{}。这个东西类似C++里的void,也有些像Java里的Object。但请注意,它是一个非指针类型。


这和java里的interface不太一样,从java过来的小伙伴们要务必了解这一点。


今天先说到这。目前感觉这两点是我最大的体会,后续肯定还有好玩的。


最后的最后

  • 我期望的结果不是朋友们从我的书、文章、博客后学会了什么知识,干成了什么,而应该是说,神农,我可是踩在你的肩膀上的喔

神农和朋友们的杂文集