Go2 设计草案介绍 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
songtianyi
V2EX    程序员

Go2 设计草案介绍

  •  
  •   songtianyi 2018-08-29 20:19:39 +08:00 8143 次点击
    这是一个创建于 2601 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写下自己的理解。大家都在吐槽泛型,我觉得还行吧,contract 的表达能力蛮强的。

    原文链接 https://github.com/songtianyi/songtianyi.github.io/blob/master/mds/techniques/go2-design-draft-introduction.md

    go2 设计草案介绍

    作者: songtianyi 2018-08-29

    前言

    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:

    package main import "fmt" func Keys(m map[interface{}]interface{}) []interface{} { keys := make([]interface{}, 0) for k, _ := range m { keys = append(keys, k) } return keys } func main() { m := make(map[string]string, 1) m["demo"] = "data" fmt.Println(Keys(m)) } 

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

    package main import "fmt" func Keys<K, V>(m map[K]V) []K { keys := make([]K, 0) for k, _ := range m { keys = append(keys, k) } return keys } func main() { m := make(map[string]string, 1) m["demo"] = "data" fmt.Println(Keys(m)) } 

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

    // rust fn print_g<T: Graph>(g : T) { println!("graph area {}", g.area()); } 

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

    // comparable contract contract Equal(t T) { t == t } // addable contract contract Addable(t T) { t + t } 

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

    func Sum(type T Addable(T))(x []T) T { var total T for _, v := range x { total += v } return total } var x []int total := Sum(int)(x) 

    得益于类型推断,在调用 Sum 时可以简写成:

    total := Sum(x) 

    contract 在使用时,如果参数是一一对应的(可推断), 也可以省略参数:

    func Sum(type T Addable)(x []T) T { var total T for _, v := range x { total += v } return total } 

    不可推断时就需要指明该 contract 是用来约束谁的:

    func Keys(type K, V Equal(K))(m map[K]V) []K { ... } 

    当然,下面的写法也可以推断,最终如何就看 Go team 的抉择了:

    func Keys(type K Equal, V)(m map[K]V) []K { ... } 

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

    错误处理

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

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

    func CopyFile(src, dst string) error { r, err := os.Open(src) if err != nil { return fmt.Errorf("copy %s %s: %v", src, dst, err) } defer r.Close() w, err := os.Create(dst) if err != nil { return fmt.Errorf("copy %s %s: %v", src, dst, err) } if _, err := io.Copy(w, r); err != nil { w.Close() os.Remove(dst) return fmt.Errorf("copy %s %s: %v", src, dst, err) } if err := w.Close(); err != nil { os.Remove(dst) return fmt.Errorf("copy %s %s: %v", src, dst, err) } } 

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

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

    r := check os.Open(src) 

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

    return fmt.Errorf("copy %s %s: %v", src, dst, err) 

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

    handle err { return fmt.Errorf("copy %s %s: %v", src, dst, err) } 

    修改后的代码为:

    func CopyFile(src, dst string) error { handle err { return fmt.Errorf("copy %s %s: %v", src, dst, err) } r := check os.Open(src) defer r.Close() w := check os.Create(dst) handle err { w.Close() os.Remove(dst) // (only if a check fails) } check io.Copy(w, r) check w.Close() return nil } 

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

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

    反馈渠道

    错误值

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

    package main import ( "fmt" "io" ) type RpcError struct { Line uint } func (s *RpcError) Error() string { return fmt.Sprintf("(%d): no route to the remote address", s.Line) } func fn3() error { return io.EOF } func fn2() error { if err := fn3(); err != nil { return &RpcError{Line: 12} } return nil } func fn1() error { if err := fn2(); err != nil { return fmt.Errorf("call fn2 failed, %s", err) } return nil } func main() { if err := fn1(); err != nil { fmt.Println(err) } } 

    此程序的输出为:

    call fn2 failed, (12): no route to the remote address 

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

    if err == io.EOF { ... } 

    或者进行类型断言:

    if pe, ok := err.(*os.PathError); ok { ... pe.Path ... } 

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

    error inspection

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

    package errors // A Wrapper is an error implementation // wrapping context around another error. type Wrapper interface { // Unwrap returns the next error in the error chain. // If there is no next error, Unwrap returns nil. Unwrap() error } 

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

    // Is reports whether err or any of the errors in its chain is equal to target. func Is(err, target error) bool // As checks whether err or any of the errors in its chain is a value of type E. // If so, it returns the discovered value of type E, with ok set to true. // If not, it returns the zero value of type E, with ok set to false. func As(type E)(err error) (e E, ok bool) 
    error formatting

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

    package errors type Formatter interface { Format(p Printer) (next error) } 

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

    func (e *WriteError) Format(p errors.Printer) (next error) { p.Printf("write %s database", e.Database) if p.Detail() { p.Printf("more detail here") } return e.Err } func (e *WriteError) Error() string { return fmt.Sprint(e) } 

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

    a href="https://github.com/golang/go/wiki/Go2ErrorValuesFeedback" rel="nofollow">反馈渠道

    第 1 条附言    2018-08-30 11:08:07 +08:00
    有些小的描述改进,可以点击查看原文链接。
    第 2 条附言    2018-08-31 17:04:54 +08:00
    有小部分内容描述有误,已更正,请查看原文链接。
    73 条回复    2018-11-16 11:50:39 +08:00
    kernel
        1
    kernel  
       2018-08-29 21:51:58 +08:00   1
    我大 JS 2 空格党表示缩进 4 个已经不爽了 go 缩 8 个。。。
    loqixh
        2
    loqixh  
       2018-08-29 22:35:12 +08:00
    Go,毫无疑问已经成为主流服务端开发语言之一 谁给的自信............
    songtianyi
        3
    songtianyi  
    OP
       2018-08-29 22:56:30 +08:00   1
    @loqixh 现在 Go 职位蛮多的,这么说没毛病吧。
    songtianyi
        4
    songtianyi  
    OP
       2018-08-29 22:57:14 +08:00   1
    @kernel 1 个 tab, 看平台怎么渲染的了。
    wenzhoou
        5
    wenzhoou  
       2018-08-29 23:04:24 +08:00 via Android
    主流是形容服务端的。再说了,还有之一嘛!
    Kilerd
        6
    Kilerd  
       2018-08-29 23:22:43 +08:00
    唯一一个用 tab 做缩进的语言,而且还是规范中强制。

    难受
    mrcode
        7
    mrcode  
       2018-08-29 23:24:21 +08:00
    @loqixh 认同楼上,谁给的自信让你加上“之一“的,分明是:毫无疑问已经成为主流服务端开发语言
    glues
        8
    glues  
       2018-08-29 23:39:50 +08:00
    这错误处理,还不如 go 1.x,虽然 1.x 已经够恶心的了
    kidlj
        9
    kidlj  
       2018-08-29 23:59:26 +08:00 via iPhone
    写得很好,清晰明了。感谢。
    blless
        10
    blless  
       2018-08-30 01:22:57 +08:00 via Android
    @loqixh 看讨论就知道 明显现在很多了,我司用 go 重构了几个 python 小服务之后就开始爆发了
    kran
        11
    kran  
       2018-08-30 01:35:45 +08:00 via iPhone
    一对比 1 的错误处理没那么差嘛
    感谢楼主,写的好
    loqixh
        12
    loqixh  
       2018-08-30 08:39:06 +08:00
    @blless 来来来, 说说市场占有率, 配得上主流二字, 少说也得 1%? 现在有 0.1%不?
    BBCCBB
        13
    BBCCBB  
       2018-08-30 08:39:38 +08:00
    有理有据,令人信服
    artandlol
        14
    artandlol  
       2018-08-30 09:09:27 +08:00
    @loqixh docker 容器的 都是 golang
    natscat
        15
    natscat  
       2018-08-30 09:15:00 +08:00
    @loqixh #12 现在滴滴头条好多 golang 了吧 不说七牛这种公司 golang 在国内真的挺热的(此处不讨论这语言咋样)
    loqixh
        16
    loqixh  
       2018-08-30 09:29:48 +08:00   1
    @artandlol 能拿出手的就一个 docker 功能都是 linux 系统实现的, go 只当了个粘合层, docker 啥语言都能写, 只是凑巧用了 go 而已...按你这方法 ruby 肯定是比 go 主流得多的后端语言 一个 gitlab 不知道把 go 秒到哪里去了
    loqixh
        17
    loqixh  
       2018-08-30 09:31:35 +08:00
    @natscat 然而除了你说的这几家其它就没有了, 而且这几家也只是系统少部分用了 go
    encro
        18
    encro  
       2018-08-30 09:36:48 +08:00
    改不如不改。有多丑啊这是。
    natscat
        19
    natscat  
       2018-08-30 09:47:09 +08:00
    @loqixh #17 https://studygolang.com/topics/3685 当然不可能和 C++ Java 那样 毕竟 C++ Java 都好长时间了 golang 用的公司真的不少
    natscat
        20
    natscat  
       2018-08-30 09:51:12 +08:00
    TiDB 这个是 PingCAP 做的 是 go; codis 原来豌豆荚做的 也是 golang https://github.com/hackstoic/golang-open-source-projects
    natscat
        21
    natscat  
       2018-08-30 09:51:51 +08:00
    国内一线互联网公司都有用到 golang
    loveCoding
        22
    loveCoding  
       2018-08-30 09:55:33 +08:00
    非常多的公司都在使用 golang 啊,尤其是基础服务相关的组件
    artandlol
        23
    artandlol  
       2018-08-30 09:55:33 +08:00
    @loqixh 杠精吗 谷歌的 kubernets 了解下 云服务行业主流的 docker 编排系统就是 kubernets。懂什么叫杀手级应用吗?
    gitlab 就我在用,在 kubernets 上跑过 gitlab-ci 吗?
    loqixh
        24
    loqixh  
       2018-08-30 09:57:30 +08:00
    @artandlol 算了 你们自嗨吧.....
    natscat
        25
    natscat  
       2018-08-30 10:00:43 +08:00
    @loqixh #24 不了解别人和你说了 就说别人自()嗨。。。(讨论的结果)
    HowToMakeLove
        26
    HowToMakeLove  
       2018-08-30 10:04:28 +08:00
    rust 里没有类的概念吧?
    `Rust 在声明 T 的时候,限定了入参的类型,即入参 g 必须是 Graph 的子类。`
    loqixh
        27
    loqixh  
       2018-08-30 10:10:02 +08:00
    @natscat 我是杠精我闭嘴, 你们随意
    zichen
        28
    zichen  
       2018-08-30 10:15:43 +08:00
    docker,kubernates,tidb,istio,geth,go 在基础架构和中间件这一层,王牌项目已经挺多的了。
    blless
        29
    blless &bsp;
       2018-08-30 10:16:17 +08:00 via Android
    @loqixh 1%我不敢说 0.1%我还真觉得有
    micean
        30
    micean  
       2018-08-30 10:16:48 +08:00
    互联网的服务端应用吧……要是在传统企业应用里写各种变态的逻辑不难受吗?
    duanquanyong
        31
    duanquanyong  
       2018-08-30 10:28:41 +08:00
    v2ex 真是个大杂烩
    nino
        32
    nino  
       2018-08-30 10:43:02 +08:00   2
    这么有营养的帖子但是回复都超没营养
    glues
        33
    glues  
       2018-08-30 10:45:14 +08:00   2
    @artandlol Linux 是 C 写的,按你们这逻辑,C 在服务端开发占有率已经超过 90% 了?
    chai2010
        34
    chai2010  
       2018-08-30 10:48:56 +08:00
    @nino #32 这是 V2EX 的特色,有人和你抬杠,就是不和你讨论技术
    songtianyi
        35
    songtianyi  
    OP
       2018-08-30 10:51:36 +08:00
    myyou
        36
    myyou  
       2018-08-30 10:52:47 +08:00
    @glues 对啊,c 就是占用那么高啊,你以为服务端是一门语言决定啊? c 占 90%其它语言就站 10%啊?
    glues
        37
    glues  
       2018-08-30 11:00:05 +08:00
    @myyou 既然是这么算的话,那 go 确实连 0.1% 都占不到啊,没毛病啊
    songtianyi
        38
    songtianyi  
    OP
       2018-08-30 11:04:23 +08:00
    @HowToMakeLove 很多时候 英文里的概念 翻译成中文之后就会让人产生这种疑惑,习惯就好。
    songtianyi
        39
    songtianyi  
    OP
       2018-08-30 11:05:50 +08:00
    @HowToMakeLove 改成 子类型 ,你可能就好接受点了。
    artandlol
        40
    artandlol  
       2018-08-30 11:20:30 +08:00
    @glues 胖虎你是不是 @ 错人了
    leveraging
        41
    leveraging  
       2018-08-30 11:32:52 +08:00
    这个泛型的写法也太复杂了吧。

    ps 帮配音 @glues: "怎么了我胖虎说的有毛病吗?"
    glues
        42
    glues  
       2018-08-30 11:54:02 +08:00
    @artandlol 我胖虎想 @ 谁就 @ 谁 (逃。。
    Biebe
        43
    Biebe  
       2018-08-30 12:09:39 +08:00 via iPhone
    @loqixh gitlab 不只有一个 rails,其余中间件如 gitaly shell 都是 go 写的
    ifaii
        44
    ifaii  
       2018-08-30 12:28:26 +08:00 via iPhone
    最大黑点就是错误处理 简直不能再烂
    jlkm2010
        45
    jlkm2010  
       2018-08-30 12:43:48 +08:00   2
    还有人在这里杠,就足以说明 go 是主流语言之一了
    rahuahua
        46
    rahuahua  
       2018-08-30 12:49:53 +08:00
    @loqixh 可以找各个大公司的招聘看看,基本上都有 go 的职位,如果这都不算主流的话,那只有 c/c++/java 才能算主流了
    simpx
        47
    simpx  
       2018-08-30 14:39:10 +08:00
    go2 的风格,有从 c 变成 c++的趋势
    cchange
        48
    cchange  
       2018-08-30 15:22:40 +08:00
    看来把 C++中不复杂的内容学通了就可以了 语言这些太多包袱了
    karllynn
        49
    karllynn  
       2018-08-30 15:30:02 +08:00
    屏蔽了某个杠精,神清气爽

    我觉得这个改动可以啊,contract 给一个最简单的泛型,够用了
    iRiven
        50
    iRiven  
       2018-08-30 16:05:27 +08:00 via Android
    泛型不太懂,看了官方的包各种类型判断,贼爽,至于错误处理还行,不是很理想,但有好过没有,后面的错误是扩展在 errors 包里还是直接在默认 error 类型上面扩展一个函数
    rockyou12
        51
    rockyou12  
       2018-08-30 16:15:56 +08:00
    泛型就学人家用尖括号定义真的不行吗……原版的 map 和 slice 的类型写着我都感觉挺难受了……

    而且错误处理虽然没有 try/catch 的多层嵌套,但读的时候却是和业务顺序是反的,感觉好不适,估计也只能多写一些菜知道好不好了……
    songtianyi
        52
    songtianyi  
    OP
       2018-08-30 16:50:46 +08:00
    @iRiven 可以说说哪里不懂嘛?
    songtianyi
        53
    songtianyi  
    OP
       2018-08-30 17:00:04 +08:00
    janxin
        54
    janxin  
       2018-08-30 17:09:14 +08:00 via iPad
    这次的 handle 明显就是一种 try catch 机制老的,就是嵌套关系十分不明显,后续程序代码理解时就难受了
    psuwgipgf
        55
    psuwgipgf  
       2018-08-30 17:42:36 +08:00
    @janxin 嗯,我也感觉不如 try catch 好用,可能是因为我是个 android 开发吧。不知道 其它流行语言是怎么处理这种情况的
    songtianyi
        56
    songtianyi  
    OP
       2018-08-30 19:31:41 +08:00
    @psuwgipgf try catch 是主流,go 的做法有点像 rust,但类型系统比不上 rust,所以看起来蹩脚
    songtianyi
        57
    songtianyi  
    OP
       2018-08-30 19:38:23 +08:00
    @janxin 的确,有一种 goto 的感觉,debug 和读起来比较费劲。
    iceheart
        58
    iceheart  
       2018-08-30 22:07:54 +08:00 via Android
    check handle 一点也不简洁。
    check 完全可以省掉。
    比如这样
    func service1()(string,error){}
    func example() error {
    handle error1 {
    .....
    }
    res1, error1 := service1()//error != nil 时 触发 error1
    ...
    }
    janxin
        59
    janxin  
       2018-08-31 06:21:16 +08:00 via iPad
    @iceheart 最大的问题应该还是 go1 代码兼容问题。基于目前的方案,go1 的代码可以直接用在 go2 上,并且编译器无需大改。
    janxin
        60
    janxin  
       2018-08-31 06:23:41 +08:00 via iPad
    @iceheart 而且还不需要考虑变量命名问题 error2 怎么办,比如可能出现的作用域问题等等
    iceheart
        61
    iceheart  
       2018-08-31 06:47:24 +08:00 via Android
    @janxin
    没懂你的意思
    看代码
    r, err := os.Open(src)
    变成草案:
    handle err { return fmt.Errorf("copy %s %s: %v", src, dst, err) }
    r := check os.Open(src)
    或者我说的
    handle err { return fmt.Errorf("copy %s %s: %v", src, dst, err) }
    r,err := os.Open(src)
    哪个看起来更简洁?
    反正我是很讨厌在每次函数调用加上个 check 前缀,这太嗦了
    janxin
        62
    janxin  
       2018-08-31 08:17:11 +08:00 via iPad
    @iceheart
    R, err := os.Open(src)
    ...
    go func(){
    err:=validate(something)
    ...
    }()
    这样需要额外改动编译器支持作用域变更

    然后还有关于 Go1 老代码拿过来用时兼容问题:原有的
    if err != nil {
    ...
    }
    基本上都变成了冗余代码

    check 和原有写 err 并没有什么本质区别,一个冗余了 err,一个冗余了 check,不过使用关键字相当于显示化标示了此处有处理细节,你仍旧在 Go2 中可以使用 Go1 的语法。如果只是使用 err,如果没有定义 handle,是按照 Go1 原有的变量未使用错误还是按照默认的 handle 处理呢?
    iceheart
        63
    iceheart  
       2018-08-31 08:42:25 +08:00 via Android
    @janxin 不明白你说的作用域问题在哪里
    相对于 新草案的
    r := check os.Open(src)
    比较一下这个
    r,err := os.Open(src)
    哪个更兼容 GO1 ?
    没有定位 handle,后边这个不就是 Go1 的语法么?
    定义了 handle 的情况,这个明确指定了 handle 过程组,难道不更好吗?
    janxin
        64
    janxin  
       2018-08-31 09:10:39 +08:00 via iPad
    @iceheart 但是如果 err 后续不使用在 go1 里编译报错的啊…
    abscon
        65
    abscon  
       2018-08-31 11:34:28 +08:00 via iPhone
    @songtianyi 你回复 @HowToMakeLove 说的『改成「子类型」』也完全不能接受。Rust 里既没有类又没有子类型。生命周期倒是粘点边,可严格说来它也不是。

    Rust 也能约束泛型参数『必须是可比较的(comparable),必须是能做加法运算(addable)的』。

    另外你说的那个 Equal 在 Rust 里不叫『可比较的』,而是『存在等价关系』。而且还能区分出『部分等价关系』和『等价关系』。『可比较的』是另外的特性,也区分了『偏序』和『全序』。而且『可做加法运算』还能指定加法右边的类型。

    现在,该是你解释一下为什么『 Go2 的泛型约束的表达能力比 Rust 的 Trait 更强大』了。
    iceheart
        66
    iceheart  
       2018-08-31 12:33:05 +08:00 via Android
    @janxin Go1 编译 Go2 代码?
    janxin
        67
    janxin  
       2018-08-31 12:55:34 +08:00 via iPad
    @iceheart 你这样编译器行为就改变了,现在只是新增,不一样的
    janxin
        68
    janxin  
       2018-08-31 12:57:21 +08:00 via iPad
    @iceheart 本质上 go 是想保证原来的行为的基础上通过新增语法解决问题,而不是改变行为
    iceheart
        69
    iceheart  
       2018-08-31 13:20:10 +08:00 via Android
    @janxin 我的意思就是省掉 check, 让用户写代码不用那么恶心,改变啥行为了? 难道加个 handle 就不改变 Go1 的行为,就能编译了?
    janxin
        70
    janxin  
       2018-08-31 14:53:00 +08:00 via iPad
    @iceheart 你不考虑 corner case 那就讨论不下去了
    songtianyi
        71
    songtianyi  
    OP
       2018-08-31 16:56:38 +08:00
    @abscon 感谢指正,subtype 只存在于 lifetime 之间,我的描述有误。Rust 可以通过实现 trait 来约束。实现 Add trait 的时候 另外一个 operand 可以指定类型。比 Rust trait 的表达能力强的说法不对,只能说相对简单一些吧。
    songtianyi
        72
    songtianyi  
    OP
       2018-08-31 17:04:04 +08:00
    @abscon @HowToMakeLove 已更正为: 即入参 g 必须是实现了 Graph 的类型
    AmorZhang
        73
    AmorZhang  
       2018-11-16 11:50:39 +08:00
    @loqixh 没用过 就不要发言了吧
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     913 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 18:52 PVG 02:52 LAX 11:52 JFK 14:52
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86