這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1. golang的指標
可以將unsafe.Pointer轉換成uintptr,然後變相做指標運算
package mainimport "fmt"import "unsafe"type User struct {Id intName 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
以上類型中,常用的類型有:os.File、strings.Reader、bufio.Reader/Writer、bytes.Buffer、bytes.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 bytefmt.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 mainimport ( "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() {// ....}
這個其實也算常識了,寫過些代碼的人可能都知道。但是在實際的代碼中,我們往往還會發現類似這樣的一些不好的寫法。因此,對於這些慣用法(或者升級為代碼規範),我覺得更重要的不是瞭解知道,而是執行和遵守。