1. golang的指针
可以将unsafe.Pointer转换成uintptr,然后变相做指针运算
package main import "fmt" import "unsafe" type User struct { Id int Name string } func main() { u := &User{1, "tom"} var up uintptr = uintptr(unsafe.Pointer(u)) + unsafe.Offsetof(u.Name) var name *string = (*string)(unsafe.Pointer(up)) fmt.Println(*name) }
2. package io
实现了io.Reader或io.Writer接口的类型(导出的类型):
- os.File 同时实现了io.Reader和io.Writer
- strings.Reader 实现了io.Reader
- bufio.Reader/Writer 分别实现了io.Reader和io.Writer
- bytes.Buffer 同时实现了io.Reader和io.Writer
- bytes.Reader 实现了io.Reader
- compress/gzip.Reader/Writer 分别实现了io.Reader和io.Writer
- crypto/cipher.StreamReader/StreamWriter 分别实现了io.Reader和io.Writer
- crypto/tls.Conn 同时实现了io.Reader和io.Writer
- encoding/csv.Reader/Writer 分别实现了io.Reader和io.Writer
- mime/multipart.Part 实现了io.Reader
WriteAt()方法简单示例:
file, err := os.Create("writeAt.txt") if err != nil { panic(err) } defer file.Close() file.WriteString("Golang中文社区——这里是多余的") n, err := file.WriteAt([]byte("Go语言学习园地"), 24) if err != nil { panic(err) } fmt.Println(n)
file.WriteString("Golang中文社区——这里是多余的") 往文件中写入Golang中文社区——这里是多余的;之后file.WriteAt([]byte("Go语言学习园地"), 24) 在文件流的offset=24处写入Go语言学习园地(会覆盖该位置的内容)。
ReadFrom()方法简单示例:
file, err := os.Open("writeAt.txt") if err != nil { panic(err) } defer file.Close() writer := bufio.NewWriter(os.Stdout) writer.ReadFrom(file) writer.Flush()WriteTo()方法示例:
reader := bytes.NewReader([]byte("Go语言学习园地")) reader.WriteTo(os.Stdout)Seek()方法示例:
reader := strings.NewReader("Go语言学习园地") reader.Seek(-6, os.SEEK_END) r, _, _ := reader.ReadRune() fmt.Printf("%c\n", r)
在标准库中,有如下类型实现了io.ByteReader或io.ByteWriter:
- bufio.Reader/Writer 分别实现了io.ByteReader和io.ByteWriter
- bytes.Buffer 同时实现了io.ByteReader和io.ByteWriter
- bytes.Reader 实现了io.ByteReader
- strings.Reader 实现了io.ByteReader
接下来的示例中,我们通过bytes.Buffer来一次读取或写入一个字节(主要代码):
var ch byte fmt.Scanf("%c\n", &ch) buffer := new(bytes.Buffer) err := buffer.WriteByte(ch) if err == nil { fmt.Println("写入一个字节成功!准备读取该字节……") newCh, _ := buffer.ReadByte() fmt.Printf("读取的字节:%c\n", newCh) } else { fmt.Println("写入错误") }Pipe()方法和PipeReader、PipeWriter结构的示例:
func main() { Pipe() } func Pipe() { pipeReader, pipeWriter := io.Pipe() go PipeWrite(pipeWriter) go PipeRead(pipeReader) time.Sleep(1e7) } func PipeWrite(pipeWriter *io.PipeWriter) { var ( i = 0 err error n int ) data := []byte("Go语言学习园地") for _, err = pipeWriter.Write(data); err == nil; n, err = pipeWriter.Write(data) { i++ if i == 3 { pipeWriter.CloseWithError(errors.New("输出3次后结束")) } } fmt.Println("close 后输出的字节数:", n, " error:", err) } func PipeRead(pipeReader *io.PipeReader) { var ( err error n int ) data := make([]byte, 1024) for n, err = pipeReader.Read(data); err == nil; n, err = pipeReader.Read(data) { fmt.Printf("%s\n", data[:n]) } fmt.Println("writer 端 closewitherror后:", err) }Copy()方法示例:
package main import ( "fmt" "io" "os" ) func main() { io.Copy(os.Stdout, strings.NewReader("Go语言学习园地")) io.Copy(os.Stdout, os.Stdin) fmt.Println("Got EOF -- bye") }
3. 程序结构
假设有一个服务的大致实现是这样的:
type Service struct { wg sync.WaitGroup } func (p *Service) Start() { p.wg.Add(1) go p.run() } func (p *Service) Close() { // ... p.wg.Wait() } func (p *Service) run() { defer p.wg.Done() // .... }其中run()是服务的主要逻辑实现。这样写法的问题是wg的Add和Done函数调用分离了,容易导致在以后的代码维护中不小心就导致Add和Done的调用不匹配从而Wait执行的结果不符合预期。从代码组织的方式上来说,应该把功能相关的代码尽可能地放在一起。
type Service struct { wg sync.WaitGroup } func (p *Service) Start() { p.wg.Add(1) go func() { p.run() p.wg.Done() }() } func (p *Service) Close() { // ... p.wg.Wait() } func (p *Service) run() { // .... }这个其实也算常识了,写过些代码的人可能都知道。但是在实际的代码中,我们往往还会发现类似这样的一些不好的写法。因此,对于这些惯用法(或者升级为代码规范),我觉得更重要的不是了解知道,而是执行和遵守。