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"

}

~~~

posted on 2024-07-14 09:04  江湖乄夜雨  阅读(19)  评论(0编辑  收藏  举报