published on
tags: tech Go

Go语言观察笔记 [中]

方法的定义

Go中可以给Struct定义方法(只要是同一包中,非结构体也可),类似给一个类添加成员。只是这个方法定义的时候不写在Struct中,只要是同一个包中的文件都可以对它扩展,为它添加方法。这个设计还是很好的。有点类似C++中的namespaceclass,你可以在class中定义public static的成员来实现和namespace同样的效果,但是namespace可以把这些内容拆到不同的地方,而不用挤在一起。

写法:

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

然后有一个新的类型:接口

type Abser interface {
    Abs() float64
}

接口类型的变量可以保存任何一个实现了其中对应函数的类型的值,有点类似duck type的感觉。这带有一点在显示的实现声明和纯动态类型中间的折衷把,属于用不那么严格的约束来换取便利。

这个玩意如何比较好的实现是个有意思的事情。初步估计应该是类似一个vtable那个的结构,每个值都是一个闭包。当然因为这里的闭包的环境实际上都是共享的,所以存一个指针就好了,恩这样创建一个新的时候也只要设这一个指针,别的设置在编译期都可以搞定。

这件事情我之前在玩lua的时候倒是干过。我用C++的模版元编程机制实现了一个把C++的类自动导入到lua中的办法,在lua里面就是用table + closure来实现的。方便我可以自己写成:

a.do_sth();
-- but not  a:do_sth()

因为luaa:do_sth这个设计实在是不爽,你就算不小心写错成.了,语法上也没任何问题,运行时搞不好都检查不出来,行为是不确定的。主要的缺点就是如果table中方法太多了,创建起来比较耗时,所以我一般就导导函数给lua,而不用什么类。

再回来看接口,tutor介绍了一个常用的叫error:

type error interface {
    Error() string
}

Tutor中这一篇到这居然就完了,往后走就是并发……

好吧,你的类型系统里是不是少了点什么? datatype呢…… 什么原来没写在tutor里,我在effective_go里找到类似的东西了:type switch

恩这个卧槽的做法如下:

func f(x int) interface{} {
    if x < 0 {
        return 3
    } else {
        return "str"
    }
}

func main() {
    var t = f(2)
    switch t.(type) {
    case string:
        fmt.Println("string")
    case int:
        fmt.Println("int")
    }
}

说实话有了这些基础的话,用起来就没什么问题了,并发这件事情并不是干什么都要用的,实际上Go的并发模型基本上是为服务器设计的,所以之后再慢慢看。