添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
会开车的西装  ·  Property ...·  3 月前    · 
跑龙套的牙膏  ·  ListView内的Android ...·  1 年前    · 

对go做过开发的朋友都很熟悉interface。这几天在网上看到了篇文章,谈到了interface与nil判等的问题。题是好题,就进一步了解了一下。

原题如下:

Nil接口并不是有Nil指针的接口

type Cat interface {
  Meow()
type Tabby struct {}
func (*Tabby) Meow() { fmt.Println("meow") }
func GetACat() Cat {
  var myTabby *Tabby = nil
  // Oops, we forgot to set myTabby to a real value
  return myTabby
func TestGetACat(t *testing.T) {
  if GetACat() == nil {
    t.Errorf("Forgot to return a real cat!")

毫无疑问,输出结果是空。也就是说GetACat()方法返回的值,不为nil。解答是“将一个指针返回了空指针”。说实话,真心没看懂!

官方对interface的定义

官方在常见问题中,对interface判断nil进行了描述:原文

  • interface的内部实现,其实有两个很核心的元素,那就是typevalue
  • interface==nil,仅当type、value均为nil,即(nil,nil)。很多时候,type有值,而value==nil,比如上题。
  • 实际开发中,不应存在type==nil,value!=nil的情况。
    因此,原题的解答应该是:为type确定了类型指针,但value依然没有赋值。

更多的疑问?

查看了一些资料,有几个困惑,需要逐个分析:
- 接口变量是否为指针类型?
- 结构体指针能否与其接口变量判等?

首先定义一个全局的接口和对应的两个实现类,便于后续的分析。

type Cat interface { Meow() //实现类1 type Tabby struct{} func (*Tabby) Meow() { fmt.Println("Tabby meow") } func GetNilTabbyCat() Cat { var myTabby *Tabby = nil return myTabby func GetTabbyCat() Cat { var myTabby *Tabby = &Tabby{} return myTabby //实现类2 type Gafield struct{} func (*Gafield) Meow() { fmt.Println("Gafield meow") } func GetNilGafieldCat() Cat { var myGafield *Gafield = nil return myGafield func GetGafieldCat() Cat { var myGafield *Gafield = &Gafield{} return myGafield

接口变量是否为指针类型?
在面对类型时,可以利用反射包(reflect)的TypeOf获取的Type,再调用Kind来了解基础结构类别。

    var (
        cat2        = GetNilTabbyCat()
    fmt.Printf("cat1 information: type=%15v,kind=%10v \n",reflect.TypeOf(cat2),reflect.TypeOf(cat2).Kind())

通过结果,我们可以知道,cat2是指针.
接口变量之间的判等

    var (
        cat1 Cat = nil
        cat2     = GetNilTabbyCat()
        cat3     = GetTabbyCat()
        cat4     = GetNilGafieldCat()
    fmt.Printf("cat1 information: nil?:%5v, type=%15v, value=%5v  \n", cat1 == nil, reflect.TypeOf(cat1), reflect.ValueOf(cat1))                                                //接口变量,type、value都是nil,所以cat1==nil
    fmt.Printf("cat2 information: nil?:%5v, type=%15v, type.kind=%5v, value=%5v  \n", cat2 == nil, reflect.TypeOf(cat2), reflect.TypeOf(cat2).Kind(), reflect.ValueOf(cat2))   //接口变量,type!=nil,所以cat2!==nil
    fmt.Printf("cat3 information: nil?:%5v, type=%15v, type.kind=%5v, value=%5v  \n", cat3 == nil, reflect.TypeOf(cat3), reflect.TypeOf(cat3).Kind(), reflect.ValueOf(cat3))   //接口变量,type!=nil, 所以cat3!=nil
    fmt.Printf("cat4 information: nil?:%5v, type=%15v, type.kind=%5v, value=%5v  \n", cat4 == nil, reflect.TypeOf(cat4), reflect.TypeOf(cat4).Kind(), reflect.ValueOf(cat4)) //接口变量,
    fmt.Printf("cat1==cat2?%5v , cat2==cat3?%5v, cat2==cat4?%5v \n", cat1 == cat2, cat2 == cat3, cat2 == cat4)
    //Output:
    //cat1 information: nil?: true, type=          <nil>, value=<invalid reflect.Value>  
    //cat2 information: nil?:false, type=    *main.Tabby, type.kind=  ptr, value=<nil>  
    //cat3 information: nil?:false, type=    *main.Tabby, type.kind=  ptr, value=&{}  
    //cat4 information: nil?:false, type=  *main.Gafield, type.kind=  ptr, value=<nil>  
    //cat1==cat2?false , cat2==cat3?false, cat2==cat4?false 

从运行结果看,接口变量之间判断,是要比较type和value的。

  • cat1的type是空,所以cat1!=cat2。
  • cat2与cat3的值不同,所以不等。
  • cat2与cat4的type不同,所以不等。
    更进一步,其实可以使用unsafe.Pointer来了解,可以很清楚的了解cat2变量的类别和值的情况,代码如下:
type iface struct {
        itype  uintptr
        ivalue uintptr
d1 := (*iface)(unsafe.Pointer(&cat1))
d2 := (*iface)(unsafe.Pointer(&cat2))
d3 := (*iface)(unsafe.Pointer(&cat3))
d4 := (*iface)(unsafe.Pointer(&cat4))
fmt.Println(d1)
fmt.Println(d2)
fmt.Println(d3)
fmt.Println(d4)
//Output:
//&{0 0} 
//&{7024192 0}  
//&{7024192 7302976}  
//&{7024128 0} 

接口变量能否与其结构体指针判等
从前面代码对比可以知道,接口变量是指针。那接口指针是否会与结构体指针相同呢?

    type iface struct {
        itype  uintptr
        ivalue uintptr
    var (
        cat1 Cat    = GetNilTabbyCat() //接口指针
        cat2        = GetTabbyCat()    //接口指针
        cat3 *Tabby = &Tabby{}         //结构体指针
    d1 := (*iface)(unsafe.Pointer(&cat1))
    d2 := (*iface)(unsafe.Pointer(&cat2))
    d3 := (*iface)(unsafe.Pointer(&cat3))
    fmt.Printf("cat1 information: nil?:%5v, type=%15v, value=%v ,%v \n", cat1 == nil, reflect.TypeOf(cat1), reflect.ValueOf(cat1), d1)                                                //接口变量,type、value都是nil,所以cat1==nil
    fmt.Printf("cat2 information: nil?:%5v, type=%15v, type.kind=%10v, value=%v ,%v \n", cat1 == nil, reflect.TypeOf(cat2), reflect.TypeOf(cat2).Kind(), reflect.ValueOf(cat2), d2)   //接口变量,type!=nil,所以cat2!==nil
    fmt.Printf("cat3 information: nil?:%5v, type=%15v, type.kind=%10v, value=%v ,%v \n", cat3 == nil, reflect.TypeOf(cat3), reflect.TypeOf(cat3).Kind(), reflect.ValueOf(cat3), d3)   //接口变量,type!=nil, 所以cat3!=nil
    fmt.Printf("cat1==cat2?:%5v, cat2==cat3?%v \n", cat1==cat2,cat2==cat3 )
    //Output:
    //cat1 information: nil?:false, type=    *main.Tabby, value=<nil> ,&{7024192 0} 
    //cat2 information: nil?:false, type=    *main.Tabby, type.kind=       ptr, value=&{} ,&{7024192 7302976} 
    //cat3 information: nil?:false, type=    *main.Tabby, type.kind=       ptr, value=&{} ,&{7302976 0} 
    //cat1==cat2?:false, cat2==cat3?true 

可以看出,结构体指针是可以与接口指针进行判等的,但要注意,尽管cat2、cat3的ivalue指向的地址不同,但比较的是具体的值,所以相等。

简单结论:

  1. 指针的判断,都涉及到type和value。
  2. 接口指针之间的判等,要基于type与value,一个不同则不等。
  3. 接口指针与其对应实现的结构体指针,可以进行判等操作。
对go做过开发的朋友都很熟悉interface。这几天在网上看到了篇文章,谈到了interface与nil判等的问题。题是好题,就进一步了解了一下。原题如下:Nil接口并不是有Nil指针的接口type Cat interface { Meow()}type Tabby struct {}func (*Tabby) Meow() { fmt.Println("meow") }func GetA
carbon 是一个轻量级、语义化、对IDE友好的日期时间处理库,是PHP Carbon库的Golang实现版本,初衷是为了摆脱Golang反人类的2006-01-02 15:04:05格式化时间设计 carbon 是一个轻量级、语义化、对IDE友好的日期时间处理库,是PHP Carbon库的Golang实现版本,初衷是为了摆脱Golang反人类的2006-01-02 15:04:05格式化时间设计 github:github.com/golang-module/carbon gitee:gitee.com/go-package/carbon go get -u gitee.com/go-package/carbon
ffmepg ffmpeg-go的golang绑定ffmpeg-go是https://github.com/kkroening/ffmpeg-python的golang端口,请检查ffmpeg_test.go以获取示例。 示例split:= Input(TestInputFile1).VFlip()。Split()split0,split1:= split.Get(“ 0”),split.Get(“ 1”)overlayFile:= Input(TestOverlayFile).Crop(10, 10、158、112)err:= Concat([] * Stream {split0.Trim(KwArgs {“ start_frame”:10,“ end_frame”:20}),split1.Trim(KwArgs {“ start_frame”:30,“ end_frame “:40})})。 覆盖(overlayFile.HFlip(),“”)。 DrawBox(50,50,120,
Confluent用于Apache KafkaTM的Golang客户端confluent-kafka-go是Confluent用于Apache Kafka和Confluent平台的Golang客户端。 特点:高性能-confluent-kafka-go是librd的轻量级包装,用于Apache KafkaTM的Confluent的Golang客户端TM confluent-kafka-go是用于Apache Kafka和Confluent平台的Confluent的Golang客户端。 特点:高性能-confluent-kafka-go是围绕librdkafka(一种经过微调的C客户端)的轻量级包装。 可靠性-编写Apache Kafka客户端时,有很多细节需要正确处理。 我们将它们放在一个地方(librdkafka),并在所有客户中利用这项工作(还有confluent-kafka-python和confluent-kafka-dotnet)。
1.1 interface简洁 interface(接口)是golang最重要的特性之一,Interface类型可以定义一组方法,但是这些不需要实 现。并且interface不能包含任何变量。 ◼ interface 是方法的集合 ◼ interface是一种类型,并且是指针类型 ◼ interface的 更重要的作用在于多态实现 ◼ interface 不能包含任何变量 1.2 interface定义 type 接口名称 interface { method1 (参数列表) 返回值列表 method2
Golang interface接口深入理解 interface 介绍 如果说goroutine和channel是Go并发的两大基石,那么接口是Go语言编程中数据类型的关键。在Go语言的实际编程中,几乎所有的数据结构都围绕接口展开,接口是Go语言中所有数据结构的核心。 Go不是一种典型的OO语言,它在语法上不支持类和继承的概念。 没有继承是否就无法拥有多态行为了呢?答案是否定的,Go语...
视频来源:B站《golang入门到项目实战 [2021最新Go语言教程,没有废话,纯干货!持续更新中...]》 一边学习一边整理老师的课程内容及试验笔记,并与大家分享,请移步至知乎网站,谢谢支持! 附上汇总贴:Go语言自学系列 | 汇总 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/484035621 这里结束和File结构体相关的文件写操作 package main import ( // 写字节数组 // 生成随机的32字节密钥和12字节的随机nonce key := make([]byte, 32) if _, err := rand.Read(key); err != nil { panic(err) nonce := make([]byte, 12) if _, err := rand.Read(nonce); err != nil { panic(err) // 明文数据 plaintext := []byte("Hello, world!") // 创建AES-GCM加密器 block, err := aes.NewCipher(key) if err != nil { panic(err) aead, err := cipher.NewGCM(block) if err != nil { panic(err) // 加密数据 ciphertext := aead.Seal(nil, nonce, plaintext, nil) // 输出加密后的数据和密钥、nonce fmt.Printf("Ciphertext: %s\n", hex.EncodeToString(ciphertext)) fmt.Printf("Key: %s\n", hex.EncodeToString(key)) fmt.Printf("Nonce: %s\n", hex.EncodeToString(nonce)) // 创建AES-GCM解密器 block, err = aes.NewCipher(key) if err != nil { panic(err) aead, err = cipher.NewGCM(block) if err != nil { panic(err) // 解密数据 plaintext, err = aead.Open(nil, nonce, ciphertext, nil) if err != nil { panic(err) // 输出解密后的数据 fmt.Printf("Plaintext: %s\n", plaintext) 注意:在实际使用中,密钥和nonce应该是固定的,而不是每次都随机生成。此外,nonce必须是唯一的,否则会导致安全问题。