Golang 学习摘录
-
学过C,python,或者Java之类语言再来学习golang应该是无压力,看看语法就能写。语法上比较特殊的如下:
-
声明变量并赋值使用 :=
a, b := 1, 2 //声明变量a,b,并且赋值1,2
a = 2 //a赋值2
-
if 不需要圆括号,并且可以执行表达式.for语句类似
if x:=1; x<2 {
}
-
String()函数<同java的toString(),对象的字符串形式>中如果有对自身的"%s"操作,将导致无穷递归调用。因为"%s"会调用String(),将对象转化为字符串形式。
type MyString string
func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", m) // Error: will recur forever. return fmt.Sprintf("MyString=%s", string(m)) // OK: note conversion.
}
string(m)将m转化为一个String对象,然后“%s”是作用在该String对象上,不是m本身,所以不回导致无穷递归
-
-
defer有点finally的味道,但是又有不同
for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) }
Deferred functions are executed in LIFO后进先出<栈> order, so this code will cause 4 3 2 1 0
func trace(s string) string { fmt.Println("entering:", s) return s } func un(s string) { fmt.Println("leaving:", s) } func a() { defer un(trace("a")) fmt.Println("in a") } func b() { defer un(trace("b")) fmt.Println("in b") a() } func main() { b() }
The arguments to the deferred function (which include the receiver if the function is a method) areevaluated when the defer executes, not when the call executes.
defer语句执行时,立即计算被defer的函数所需要的所有参数,而不是等到被defer的函数执行才计算该函数的参数。结果如下
entering: b in b entering: a in a leaving: a leaving: b
-
new and make
new(T) returns a pointer to a newly allocated zero value of type T。new适合初始化0值对象,new(File) and &File{} are equivalent。new([]int) returns a pointer to a newly allocated, zeroed slice structure, that is, a pointer to a nil slice value.
make(T, args) creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T (not *T)
-
Arrays
Arrays are values. Assigning one array to another copies all the elements. 数组不是引用,是值,数组a赋值给b,直接拷贝成另一份b,结果是数组a,b独立,内容相同
In particular, if you pass an array to a function, it will receive a copy of the array, not a pointer to it. 数组作为参数,传递的也是整个数组的值的拷贝,而不是指针
以上两点与C语言不同,每次拷贝数组代价昂贵,如果希望类似C语言,可以传递指针。
-
Slices
Slices行为才与C的数组一致,是指针传递。Slices更常用。Slices底层是数组,只要不超过底层数组的容量,Slices地址不变。一旦添加元素导致超过底层数组的容量,就会reallocated重新分配。默认底层数组容量为cap(slice)。
-
特殊语法...(三个点的省略号)
x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)//去掉...报错,因为y不是int类型
fmt.Println(x)//有点类似Python对于字典**dict
-
init函数
每个源代码文件都可以有一个init函数,在该源代码文件的import语句执行完后,并且变量调用其initializers后执行init函数。一般用于检查该文件中的各种状态是否正确
-
import for side effect
有时候import一个library,并不是为了使用某个函数或者类,仅仅只是希望该library的init函数执行一次,做一下准备工作:
import _ "net/http/pprof"//during its init function, the net/http/pprof package registers HTTP handlers that provide debugging information
-
Channel
管道常用于goroutine之间的通信,同步;Go不推荐使用共享内存,互斥元等方式通信。管道用于goroutine同步例子:
c := make(chan int) // 初始化一个不带缓存的管道 go func() { list.Sort() c <- 1 // Send a signal; value does not matter. }() //新起一个goroutine,去做排序,排序完后向管道写入数据 doSomethingForAWhile() <-c // 阻塞等待管道有数据写入,即等待上面goroutine排序完成
管道用作semaphore(信号量),比如限流,例子:
var sem = make(chan int, 100) func Serve(queue chan *Request) { for req := range queue { tmp := req // Create new instance of req for every goroutine. sem <- 1 //信号量满时写入阻塞 go func() { process(tmp) <-sem //处理完一个请求释放一次信号量 }() } }
由于管道sem分配了buffer,size为100.所以前100个请求,可以不阻塞立即得到执行。后面的请求必须排队等待前面100个请求当中,有请求处理完毕(管道中有数据被读出),才能写入管道,得到处理。所以,这个例子,管道起到限流作用:并行处理的请求数不得超过100
-
panic recover
Go的异常处理机制,同try...except...
Because recover always returns nil unless called directly from a deferred function, deferred code can call library routines that themselves use panic and recover without failing.
func safelyDo(work *Work) { defer func() { if err := recover(); err != nil { log.Println("work failed:", err) } }() do(work) }
do函数里出了panic异常,执行deferred函数func,func函数里,调用recover捕获异常。
版权声明:本文为博主原创文章,未经博主允许不得转载。
有疑问加站长微信联系(非本文作者)