leiiwang

参考GolangFrequently Asked Questions ,很好的参考文档,理解Golang必读。



1. Golang的特点和发起目的

Golang的特点以及要解决的问题概括起来就是三点: 1. concurrent : 多核 解决方式-> 语言层级并发, goroutine 2. garbage-collected language : c/c++的不足 3. fast compilation : c/c++等的不足, 依赖简单,类型系统简单,非传统OO。开发更简单快捷。 这种简单设计的特点很容易让人和C++对比,对比C++就是砍了90%特性,减少90%的麻烦。更好的对比可能是C,better c with goroutine and garbage-collection。

2. Golang设计原则

  1. felicity of programming : 尽可能的简化代码编写规则,这点在各种解释语言,c++11等里面都可以体现一部分了,在golang上的体现就是如包的定义,编译安装,没有头文件,no forward declarations,:= 类型推断等等
  2. orthogonality of concepts : 另一个原则是概念设计尽可能正交orthogonal,这样理解使用会更简单。 Methods can be implemented for any type; structures represent data while interfaces represent abstraction; and so on. Orthogonality makes it easier to understand what happens when things combine.当然一旦设计正交,需要的概念也变得很少。
  3. speed of compilation

3. 为什么没有泛型

Generics may well be added at some point. We don’t feel an urgency for them。为什么需要泛型可以参考这篇文章,但是泛型可以带来好处也会增加复杂度,golang可能以后会添加泛型支持,目前是一个open issue。

4. 为什么没有exceptions

这点是golang遭受用户(尤其是c++,java用户)诟病的重要原因,实际上Golang提供了panic,recover语法类似try catch。但是个人理解为什么没有只是一个选择问题,而不是技术问题。在很多语言的编码风格里尤其是Objective-C,一般都是使用Error Object来传递错误,虽然现在try catch的性能损失可以忽略不计,但是try catch的坏处是容易滥用,导致用户忽略error和exception的区别,另外Golang提供的多返回值也方便了error传递这种风格的使用,我个人对这种设计并不反感。

5. 为什么没有assertion

和没有exceptions有点类似: programmers use them as a crutch to avoid thinking about proper error handling and reporting,当然这种牵强的说法作者自己也有点没底气:We understand that this is a point of contention. There are many things in the Go language and libraries that differ from modern practices, simply because we feel it’s sometimes worth trying a different approach。

6. 为什么build concurrency on the ideas of CSP

(Communicating Sequential Processes) 一是Erlang,Occam 等的经验,二是便于语言层面构建。

7. Why goroutines instead of threads

把线程控制的复杂度从用户空间转移到语言层面(用户层面不需要关系协程还是线程,让并发的使用简单和高效是其设计目的)。The Go scheduler

8. 为什么map非线程安全

知道map非线程安全就行了,这种设计大都是一个trade off

9. Golang是面对对象语言吗

这个问题不太对,面对对象更多的是一种设计,而非语言特性,只是不同语言的实现和支持有所不同。在Golang里面的方式是interface,无type hierarchy, subclass的方式有点类似C,比C++等更general。同时Golang里面的Method也更general,可以给任何类型添加方法,总的来说的就是轻量级,更简单却能做更多事。个人觉得是Golang里最美的设计,面试官再问你会不会面对对象编程,你就打他脸。

10. 为什么Golang没有运算符或者方法重载

没什么好说的,运算符和方法重载没什么用。即使在提供这类功能的语言里面也不要使用(除非一些极端场合如数据处理框架重载+*符号用于运算矩阵,这个场合非常少,更多时候这种功能大概会带来1%的便利和1000%的代码混乱和可读性降低)。

11. interface的一个有疑问的例子Why doesn’t type T satisfy the Equal interface

type Equaler interface {
    Equal(Equaler) bool
}
type T int
func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler

因为Equaler的Equal函数需要的类型不同,正确的实现方式为

type T2 int
func (t T2) Equal(u Equaler) bool { return t == u.(T2) }  // satisfies Equaler

另一个例子
type Opener interface {
   Open() Reader
}

func (t T3) Open() *os.File
//T3 does not satisfy Opener, although it might in another language.

Golang没有自动类型转换,也没有多态(类之间),这也是作者的trade off

12. Error和nil的一个例子

func returnsError() error {
	var p *MyError = nil
	if bad() {
		p = ErrBad
	}
	return p // Will always return a non-nil error.
}

这和interface的实现有关:

Under the covers, interfaces are implemented as two elements, a type and a value. The value, called the interface’s dynamic value, is an arbitrary concrete value and the type is that of the value. For the int value 3, an interface value contains, schematically, (int, 3). An interface value is nil only if the inner value and type are both unset, (nil, nil). In particular, a nil interface will always hold a nil type. If we store a nil pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (*int, nil). Such an interface value will therefore be non-nil even when the pointer inside is nil.

一个更直观点的例子
type myError struct{}
func (this *myError) Error() string { return "" }
var error1 *myError = nil
var error2 error = error1// to interface => error2 != nil

13. go不支持tagged or untagged union

untagged union不安全,tagged union或者Variant types, algebraic type则和interface有重合

14. 1为什么没有隐式类型转换

作者认为这种功能带来的隐患比便利要多,Golang中Int和int64都不是一个类型,不能隐式转换。

15. Why are maps, slices, and channels references while arrays are values?

知道这回事就行了slices的实现方式是对底层array的引用(见下图), 参考 此处输入图片的描述

理解了上图再看这个例子就明白了
sa := make([]int, 10)
fmt.Println("sa:", saa)//saa: [0 0 0 0 0 0 0 0 0 0]
sb := saa[1:8]
sb[2] = 2
sb = append(sbb, 8) 
//sa 也被修改了
fmt.Println("sa:", sa, "sb:", sb)// saa: [0 0 0 2 0 0 0 0 8 0] sbb: [0 0 2 0 0 0 0 8]

16. When should I use a pointer to an interface

Almost never.传一个指针指向interface大都数时候都是错误。另:The insight is that although a pointer to a concrete type can satisfy an interface, with one exception a pointer to an interface can never satisfy an interface.

17. 关于闭包的一个例子

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}
// 输出是c c c 因为v共用一个变量 而输出取决于fmt.Println调用的时候v存储的数值
    // 正确的写法为
    for _, v := range values {
        go func(u string) {
            fmt.Println(u)
            done <- true
        }(v)
    }
    // 或者做一个本地拷贝
    for _, v := range values {
        v := v // create a new 'v'. 注意位置 写在func里面还是不对
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

18. 没有?:操作符

差评

comments powered by Disqus