Go新手容易踩的坑(函数与方法)
方法的接收器 —— 对象接收器与指针接收器
对象接收器不会更新属性
package tests import ( "fmt" "testing" ) type Consumer struct { Balance int64 } // 对象接收器 func (c Consumer) add(v int64) { c.Balance += v } func TestT1(t *testing.T) { c := Consumer{Balance: 10} // 不会改变... c.add(100) fmt.Println("c.Balance:>> ", c.Balance) // 10 }
指针接收器可以更新属性
package tests import ( "fmt" "testing" ) type Consumer struct { Balance int64 } // 指针接收器 func (c *Consumer) add(v int64) { c.Balance += v } func TestT1(t *testing.T) { c := Consumer{Balance: 10} // 会改变... c.add(100) fmt.Println("c.Balance:>> ", c.Balance) // 110 }
返回一个nil接收器(注意这个接收器实际上是有类型的)
package tests import ( "errors" "fmt" "strings" "testing" ) type Consumer struct { Name string Age int } type MultiError struct { errs []string } func (m *MultiError) add(err error) { m.errs = append(m.errs, err.Error()) } // 实现 error 接口 func (m *MultiError) Error() string { return strings.Join(m.errs, ";") } func (c *Consumer) Validate() error { var m *MultiError if c.Name == "" { m = &MultiError{} m.add(errors.New("名字是空!")) } if c.Age == 0 { if m == nil { m = &MultiError{} } m.add(errors.New("年龄是0!")) } return m } func TestT1(t *testing.T) { c := Consumer{Name: "naruto", Age: 10} if err := c.Validate(); err != nil { // Notice 原因是:这个err有类型!(go里面一个变量有2个属性:type与value,虽然value是nil,但是它的type是*MultiError) fmt.Println("发生了err: ", err) // 发生了err: <nil> } }
忽略defer语句参数和接收器的计算1 —— 参数计算
有问题的代码
package tests import ( "errors" "fmt" "testing" ) const ( StatusSuccess = "success" StatusErrorFoo = "error_foo" StatusErrorBar = "error_bar" ) func notify(s string) { fmt.Println("执行了notify, s: ", s) } func incrementCounter(s string) { fmt.Println("执行了incrementCounter, s: ", s) } func foo() error { //return nil return errors.New("foo的报错...") } func f() error { var status string // Notice status参数是立即被计算的,所以这两个方法中的status参数是空字符串!!! defer notify(status) defer incrementCounter(status) if err := foo(); err != nil { status = StatusErrorFoo return err } status = StatusSuccess return nil } func TestT1(t *testing.T) { f() // // 打印了空字符串!!! /* 执行了incrementCounter, s: 执行了notify, s: */ }
修改方案1:使用指针
package tests import ( "errors" "fmt" "testing" ) const ( StatusSuccess = "success" StatusErrorFoo = "error_foo" StatusErrorBar = "error_bar" ) func notify(s *string) { fmt.Println("执行了notify, s: ", *s) } func incrementCounter(s *string) { fmt.Println("执行了incrementCounter, s: ", *s) } func foo() error { //return nil return errors.New("foo的报错...") } func f() error { var status string // Notice 传 字符串指针 defer notify(&status) defer incrementCounter(&status) if err := foo(); err != nil { status = StatusErrorFoo return err } status = StatusSuccess return nil } func TestT1(t *testing.T) { f() /* 执行了incrementCounter, s: error_foo 执行了notify, s: error_foo */ }
修改方案2:将闭包作为defer语句调用
package tests import ( "errors" "fmt" "testing" ) const ( StatusSuccess = "success" StatusErrorFoo = "error_foo" StatusErrorBar = "error_bar" ) func notify(s string) { fmt.Println("执行了notify, s: ", s) } func incrementCounter(s string) { fmt.Println("执行了incrementCounter, s: ", s) } func foo() error { //return nil return errors.New("foo的报错...") } func f() error { var status string // Notice 将闭包作为defer语句调用 // 闭包:对外部环境变量引用的函数 defer func() { notify(status) incrementCounter(status) }() if err := foo(); err != nil { status = StatusErrorFoo return err } status = StatusSuccess return nil } func TestT1(t *testing.T) { f() /* 执行了incrementCounter, s: error_foo 执行了notify, s: error_foo */ }
忽略defer语句参数和接收器的计算2 —— 指针和值接收器
值接收器的情况
package tests import ( "fmt" "testing" ) type Consumer struct { Name string } // 值接收器 func (c Consumer) printName() { fmt.Println("name: ", c.Name) } func TestT1(t *testing.T) { c := Consumer{Name: "foo"} // defer 立即获取当前的值... defer c.printName() // foo c.Name = "bar" }
指针接收器的情况
package tests import ( "fmt" "testing" ) type Consumer struct { Name string } // 值接收器 func (c *Consumer) printName() { fmt.Println("name: ", c.Name) } func TestT1(t *testing.T) { c := Consumer{Name: "foo"} // defer 注意这里是指针... defer c.printName() // bar c.Name = "bar" }
~~~