§ 0x01 学习语言的方法
不知道某个细节该怎么写时:
- 自己想,去推测。
- 带着问题,去看源码。
- 带着问题,去请教有经验的人。 导师或者周围的人。
学习的点:
- 代码布局;
- 函数实现与抽象;
- 通过的编程范式如何使用;
- 其他意外的收获。
§ 0x02 bufio
上一篇为介绍看archive的总结:https://my.oschina.net/linhaha/blog/5120097
Q1: 常量定义放在头部还是靠近使用的位置?
A1:靠近使用的位置。可以看bufio.go。
const minReadBufferSize = 16
const maxConsecutiveEmptyReads = 100
// NewReaderSize returns a new Reader whose buffer has at least the specified
// size. If the argument io.Reader is already a Reader with large enough
// size, it returns the underlying Reader.
func NewReaderSize(rd io.Reader, size int) *Reader {
// Is it already a Reader?
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
return b
}
if size < minReadBufferSize {
size = minReadBufferSize
}
r := new(Reader)
r.reset(make([]byte, size), rd)
return r
}
Q2: 注释写不写?
A2:见名知义则不写,必要不能通过代码表达的要写。不一定是变量,也可能是逻辑。如Q1中的类型转换的注释。如Reset函数,包含的范围可能很大,可以在注释中说明下,具体做了什么,这是调用者关心的。
Q3: 指针调用过程中能否改变指针的指向?
A3:可以,见reset函数,通过创建新的对象实现reset。大家可以思考下,比逐个去reset各个成员的好处。
func (b *Reader) reset(buf []byte, r io.Reader) {
*b = Reader{
buf: buf,
rd: r,
lastByte: -1,
lastRuneSize: -1,
}
}
Q4:循环里return,合理吗?
A4:合理,见fill函数。
Q5:readErr执行后,将err重置为nil了,是否合理?
A5:看情况。Unix的read调用也是会修改文件描述符的当前偏移量的。所以这里在一个读操作过程中,修改了状态看起来了合理。但可能不太符合常规,所以最好是有注释说明,源码里却没有。
Q6:任何场景下,都要考虑并发安全吗?
A6:不是。bufio这种接口,一般是不会共享给多个协程,同样的操作系统的read调用也不保证是并发安全,所以不用加锁。
Q7:如何优雅处理err?避免到处return err。
A7:通过将err定义为struct实例的成员,提供接口统一获取err。这也是coolshell上推荐的。见 https://coolshell.cn/articles/21140.html
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}
§ 0x03 小结
通过学习标准库的源码,可以打破现有对Go的认识,可最快速度提升Go水平。