3

Go2设计草案介绍

 3 years ago
source link: https://studygolang.com/articles/34840
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Go2设计草案介绍

mob604756f0bbf4 · 大约5小时之前 · 21 次点击 · 预计阅读时间 3 分钟 · 大约8小时之前 开始浏览    

前言
Go,毫无疑问已经成为主流服务端开发语言之一,但它的类型特性却少的可怜,仅支持 structural subtyping。在 TIOBE 排名前二十的语言中,不管是上古语言 Java, 还是 2010 年之后出现的新语言 Rust/Julia 等,都支持至少三种类型特性,对此社区抱怨很多,另外还有它的错误处理方式,以及在 Go1.11 版本才解决的依赖管理等问题。在最近的 GopherCon2018 上,官方放出了解决这些问题的草案 (draft),这些内容还没有成为正式的提案 (proposal), 只是先发出来供大家讨论,最终会形成正式提案并被逐步引入到后续的版本中。此次放出的草案,集中讨论了三个问题,泛型 / 错误处理 / 错误值。

泛型
泛型是复用逻辑的一个有效手段,在 2016 和 2017 年的 Go 语言调查中,泛型都列在最迫切的需求之首,在 Go1.0 release 之后 Go team 就已经开始探索如何引入泛型,但同时要保持 Go 的简洁性 (开发者喜爱 Go 的主要原因之一),之前的几种实现方式都存在严重的问题,被废弃掉了,所以进展并不算快,甚至导致部分人误解为 Go team 并不打算引入泛型。现在,最新的草案经过半年的讨论和优化,已经确认可行 (could work),我们期待已久的泛型几乎是板上钉钉的事情了,那么 Go 的泛型大概长什么样?

在没有泛型的情况下,通过 interface{}是可以解决部分问题的,比如 ring的实现,但这种方法只适合用在数据容器里, 且需要做类型转换。当我们需要实现一个通用的函数时,就做不到了,例如实现一个函数,其返回传入的 map 的 key:

Go2设计草案介绍

这样写连编译都通过不了,因为类型不匹配。那么参考其他支持泛型的语言的语法,可以这样写:

Go2设计草案介绍
但是这种写法是有缺陷的,假设 append 函数并不支持 string 类型,就可能会出现编译错误。我们可以看下其他语言的做法:

Go2设计草案介绍
Rust 在声明 T 的时候,限定了入参的类型,即入参 g 必须是 Graph 的子类。和 Rust 的 nominal subtyping 不同,Go 属于 structural subtyping,没有显式的类型关系声明,因此不能使用此种方式。Go 在草案中引入了 contract来解决这个问题,语法类似于函数, 写法更复杂,但表达能力比 Rust 要更强:

Go2设计草案介绍
上述代码分别约束了 T 必须是可比较的 (comparable),必须是能做加法运算(addable) 的。使用方式很简单, 定义函数的时候加上约束即可:

Go2设计草案介绍
得益于类型推断,在调用 Sum 时可以简写成:

Go2设计草案介绍
contract 在使用时,如果参数是一一对应的 (可推断), 也可以省略参数:

Go2设计草案介绍
不可推断时就需要指明该 contract 是用来约束谁的:

Go2设计草案介绍
当然,下面的写法也可以推断,最终如何就看 Go team 的抉择了:

Go2设计草案介绍
关于实现方面的内容,这里不再讨论,留给高手吧。官方开通了反馈渠道,可以去提意见,对于我来说,唯一不满意的地方是显式的 type关键字, 可能是为了方便和后边的函数参数相区分吧。

错误处理
健壮的程序需要大量的错误处理逻辑,在极端情况下,错误处理逻辑甚至比业务逻辑还要多,那么更简洁有效的错误处理语法是我们所追求的。

先看下目前 Go 的错误处理方式,一个拷贝文件的例子:

Go2设计草案介绍

上述代码中,错误处理的代码占了总代码量的接近 50%!

Go 的 assignment-and-if-statement错误处理语句是罪魁祸首,草案引入了 check表达式来代替:

Go2设计草案介绍

但这只代替了赋值表达式和 if 语句,从之前的例子中我们可以看到,有四行完全相同的代码:

Go2设计草案介绍

它是可以被统一处理的, 于是 Go 在引入 check的同时引入了 handle语句:

Go2设计草案介绍

修改后的代码为:

Go2设计草案介绍
check 失败后,先被执行最里层的 (inner most) 的 handler,接着被上一个(按照语法顺序)handler 处理,直到 handler 执行了 return语句。

Go team 对该草案的期望是能够减少错误处理的代码量, 且兼容之前的错误处理方式, 要求不算高,这个设计也算能接受吧。

错误值
Go 的错误值目前存在两个问题。一,错误链 (栈) 没有被很好地表达;二,缺少更丰富的错误输出方式。在该草案之前,已经有不少第三方的 package 实现了这些功能,现在要进行标准化。目前,对于多调用层级的错误,我们使用 fmt.Errorf 或者自定义的 Error 来包裹它:

Go2设计草案介绍Go2设计草案介绍

此程序的输出为:

Go2设计草案介绍

很明显的问题是,我们在 main 函数里对 error 进行处理的时候不能进行类型判断, 比如使用 if 语句判断:

Go2设计草案介绍

或者进行类型断言:

Go2设计草案介绍

它是一个 RpcError 还是 io.EOF? 无从知晓。一大串的错误信息,人类可以很好地理解,但对于程序代码来说就很困难。

error inspection
草案引入了一个 error wrapper 来包裹错误链, 它相当于一个指针,将错误栈链接起来:

Go2设计草案介绍

每个层级的 error 都实现这个 wrapper,这样在 main 函数里,我们可以通过 err.Unwrap() 来获取下一个层级的 error。另外,草案引入了两个函数来简化这个过程:

Go2设计草案介绍

error formatting
有时候我们需要将错误信息分类,因为某些情况下你需要所有的信息,某些情况下只需要部分信息,因此草案引入了一个 interface:

Go2设计草案介绍

error 类型可以实现 Format 函数来打印更详细的信息:

Go2设计草案介绍

在你使用 fmt.Println("%+v", err)打印错误信息时,它会调用 Format 函数。


有疑问加站长微信联系(非本文作者)

280

本文来自:51CTO博客

感谢作者:mob604756f0bbf4

查看原文:Go2设计草案介绍

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK