if err != nil
if err != nil
func parse(r io.Reader) (*Point, error) {
var p Point
if err := binary.Read(r, binary.BigEndian, &p.Longitude); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.Latitude); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.Distance); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.ElevationGain); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.ElevationLoss); err != nil {
return nil, err
}
}
通过 Closure 处理 error
我们可以通过 Closure 的方式来处理 error:
func parse(r io.Reader) (*Point, error) {
var p Point
var err error
read := func(data interface{}) {
if err != nil {
return
}
err = binary.Read(r, binary.BigEndian, data)
}
read(&p.Longitude)
read(&p.Latitude)
read(&p.Distance)
read(&p.ElevationGain)
read(&p.ElevationLoss)
if err != nil {
return &p, err
}
return &p, nil
}
read
将 error 定义在 Receiver 中
bufio.Scanner 源码示例
bufio.Scanner()
func main() {
// An artificial input source.
const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"
scanner := bufio.NewScanner(strings.NewReader(input))
// Set the split function for the scanning operation.
scanner.Split(bufio.ScanWords)
// Count the words.
count := 0
for scanner.Scan() {
count++
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
fmt.Printf("%d\n", count)
}
// Output: 15
if err != nilscanner.Err()
bufio.Scanner
type Scanner struct {
r io.Reader // The reader provided by the client.
split SplitFunc // The function to split the tokens.
maxTokenSize int // Maximum size of a token; modified by tests.
token []byte // Last token returned by split.
buf []byte // Buffer used as argument to split.
start int // First non-processed byte in buf.
end int // End of data in buf.
err error // Sticky error.
empties int // Count of successive empty tokens.
scanCalled bool // Scan has been called; buffer is in use.
done bool // Scan has finished.
}
bufio.Scan()Scan()err
func (s *Scanner) Scan() bool {
if s.done {
return false
}
s.scanCalled = true
// 循环处理
for {
// 仅当没有 error 的时候才处理
if s.end > s.start || s.err != nil {
// process
}
}
// process
}
demo 示例
bufio.Scanner
// 定义 receiver
type Point struct {
r io.Reader
err error
}
func (r *Reader) read(data interface{}) {
if r.err == nil {
r.err = binary.Read(r.r, binary.BigEndian, data)
}
}
func parse(input io.Reader) (*Point, error) {
var p Point
r := Reader{r: input}
r.read(&p.Longitude)
r.read(&p.Latitude)
r.read(&p.Distance)
r.read(&p.ElevationGain)
r.read(&p.ElevationLoss)
if r.err != nil {
return nil, r.err
}
return &p, nil
}
个人认为上面的改造对于这个 demo 来说是不合适的:它让代码的整体可读性变差了。
bufio.Scanner
recevier 中定义 error + 流式编程
User
db.Model(&User{}).First(&result)
通过把 error 定义在 Receiver 中,我们也可以将 demo 改造成这种流式编程的风格:
// 定义 receiver
type Point struct {
r io.Reader
err error
}
func (r *Reader) read(data interface{}) *Reader {
if r.err == nil {
r.err = binary.Read(r.r, binary.BigEndian, data)
}
return r
}
func parse(input io.Reader) (*Point, error) {
var p Point
r := Reader{r: input}
r = r.read(&p.Longitude).
read(&p.Latitude).
read(&p.Distance).
read(&p.ElevationGain).
read(&p.ElevationLoss)
if r.err != nil {
return nil, r.err
}
return &p, nil
}
read()
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
// 长度不够,少一个Weight
var b = []byte {0x48, 0x61, 0x6f, 0x20, 0x43, 0x68, 0x65, 0x6e, 0x00, 0x00, 0x2c}
var r = bytes.NewReader(b)
type Person struct {
Name [10]byte
Age uint8
Weight uint8
err error
}
func (p *Person) read(data interface{}) {
if p.err == nil {
p.err = binary.Read(r, binary.BigEndian, data)
}
}
func (p *Person) ReadName() *Person {
p.read(&p.Name)
return p
}
func (p *Person) ReadAge() *Person {
p.read(&p.Age)
return p
}
func (p *Person) ReadWeight() *Person {
p.read(&p.Weight)
return p
}
func (p *Person) Print() *Person {
if p.err == nil {
fmt.Printf("Name=%s, Age=%d, Weight=%d\n",p.Name, p.Age, p.Weight)
}
return p
}
func main() {
p := Person{}
p.ReadName().ReadAge().ReadWeight().Print()
fmt.Println(p.err) // EOF 错误
}
到这里流式编程应该已经解释的足够清楚了,需要注意的是,这种编程方法的使用场景是有局限的:
- 它只适用于对于同一个业务对象的不断操作,在此基础上简化错误处理。
如果涉及多个业务对象,那么可能需要再仔细设计过整体的错误处理方式。