美文网首页
golang函数接收器及安全问题 2022-10-20

golang函数接收器及安全问题 2022-10-20

作者: 9_SooHyun | 来源:发表于2022-10-19 12:07 被阅读0次

    不管接收者类型是值类型还是指针类型,都可以通过值类型或指针类型调用

    package main
    
    import "fmt"
    
    type Person struct {
        age int
    }
    
    func (p Person) howOld() int {
        return p.age
    }
    
    func (p *Person) growUp() {
        p.age += 1
    }
    
    func main() {
        // qcrao 是值类型
        qcrao := Person{age: 18}
    
        // 值类型 调用 接收者也是值类型 的方法
        fmt.Println(qcrao.howOld()) // 18
    
        // 值类型 调用 接收者是指针类型 的方法
        qcrao.growUp()
        fmt.Println(qcrao.howOld()) // 19
    
        // ----------------------
    
        // stefno 是指针类型
        stefno := &Person{age: 100}
    
        // 指针类型 调用接收者是值类型的方法
        fmt.Println(stefno.howOld()) // 100
    
        // 指针类型 调用接收者也是指针类型的方法
        stefno.growUp()
        fmt.Println(stefno.howOld()) // 101
    }
    
    

    实际上,当类型和方法的接收者类型不同时,其实是编译器在背后做了一些工作:

    值接收者 指针接收者
    值类型调用者 方法会使用调用者的一个副本,类似于“传值” 使用值的引用来调用方法,上例中,qcrao.growUp() 实际上是 (&qcrao).growUp()
    指针类型调用者 指针被解引用为值,上例中,stefno.howOld() 实际上是 (*stefno).howOld() 实际上也是“传值”,方法里的操作会影响到调用者,类似于指针传参,拷贝了一份指针

    因此,不管接收者类型是值类型还是指针类型,都可以通过值类型或指针类型调用,这里面实际上通过语法糖起作用的
    但需要注意的是,虽然go允许解引或者取引来实现调用,我们仍然需要关注里面的安全问题:

    • 指针可以调用值接收器方法,这不会产生安全问题,因为指针被解引用为值,该值被拷贝一份副本传给接收器;
    • 而值调用指针接收器方法,有可能产生安全问题,因为值被取了指针,接收器获取了该指针,就可能对原对象造成破坏

    go interface 对 receiver 的安全限制

    go的interface就进行了严格的安全限制,不允许接口提供的方法对接口本身产生任何可能的破坏
    就像我们在使用公牛插座的时候,插座本身需要保证插座提供的功能不会破坏插座本身
    因此,以下的接口实现是非法的

    package main
    
    import "fmt"
    
    type coder interface {
        code()
        debug()
    }
    
    type Gopher struct {
        language string
    }
    
    func (p Gopher) code() {
        fmt.Printf("I am coding %s language\n", p.language)
    }
    
    func (p *Gopher) debug() {
        fmt.Printf("I am debuging %s language\n", p.language)
    }
    
    func main() {
        var c coder = Gopher{"Go"} // var c coder = Gopher{"Go"} will err: Gopher does not implement coder (debug method has pointer receiver). this happens because a value calling a pointer receiver's function is unsafe
        c.code()
        c.debug() // c被取引用以调用debug(),是不安全的
    }
    
    

    var c coder = &Gopher{"Go"}则可以正常实现coder接口,因为它调用任何接口方法都是安全的

    package main
    
    import "fmt"
    
    type coder interface {
        code()
        debug()
    }
    
    type Gopher struct {
        language string
    }
    
    func (p Gopher) code() {
        fmt.Printf("I am coding %s language\n", p.language)
    }
    
    func (p *Gopher) debug() {
        fmt.Printf("I am debuging %s language\n", p.language)
    }
    
    func main() {
        var c coder = &Gopher{"Go"} 
        c.code() // c被解引用以调用code(),是安全的
        c.debug()
    }
    

    抛弃了接口后的以下常规代码,Gopher可以正常调用*Gopher.debug,这是因为上面提到的,编译器自动对c做了引用。常规代码的安全,go交给了开发者

    package main
    
    import "fmt"
    
    type Gopher struct {
        language string
    }
    
    func (p Gopher) code() {
        fmt.Printf("I am coding %s language\n", p.language)
    }
    
    func (p *Gopher) debug() {
        p.language = "go1.18"
        fmt.Printf("I am debuging %s language\n", p.language)
    }
    
    func main() {
        var c = Gopher{"Go"}
        c.code()  // I am coding Go language
        c.debug() // I am debuging go1.18 language
        
        // 值调用指针接收器方法,产生安全问题,因为值被取了指针,接收器获取了该指针,对原对象造成修改
        fmt.Println("final language: ", c.language) // final language:  go1.18, which means c has been changed.
        
    }
    

    小结

    从语法角度讲,不管接收者类型是值类型还是指针类型,都可以通过值类型或指针类型调用

    而实际上,我们在编程实践中,应该遵循interface一样的规范:
    避免隐式地取引调用,保证安全

    相关文章

      网友评论

          本文标题:golang函数接收器及安全问题 2022-10-20

          本文链接:https://www.haomeiwen.com/subject/upluzrtx.html