当前位置: 首页 > news >正文

微网站可以做成域名访问网上接单平台

微网站可以做成域名访问,网上接单平台,如何零基础学编程,北京seo公司优化网络可见性现象 在日常开发中,可能一不小心就会掉进 Go 语言的某些陷阱里,而本文要介绍的 nil ≠ nil 问题,便是其中一个,初看起来会让人觉得很诡异,摸不着头脑。 先来看个例子: type CustomizedError struct {Err…

现象

在日常开发中,可能一不小心就会掉进 Go 语言的某些陷阱里,而本文要介绍的 nil ≠ nil 问题,便是其中一个,初看起来会让人觉得很诡异,摸不着头脑。

先来看个例子:

type CustomizedError struct {ErrorCode intMsg       string
}func (e *CustomizedError) Error() string {return fmt.Sprintf("err code: %d, msg: %s", e.ErrorCode, e.Msg)
}
func main() {txn, err := startTx()if err != nil {log.Fatalf("err starting tx: %v", err)}if err = txn.doUpdate(); err != nil {log.Fatalf("err updating: %v", err)}if err = txn.commit(); err != nil {log.Fatalf("err committing: %v", err)}fmt.Println("success!")
}type tx struct{}func startTx() (*tx, error) {return &tx{}, nil
}func (*tx) doUpdate() *CustomizedError {return nil
}func (*tx) commit() error {return nil
}

这是一个简化过了的例子,在上述代码中,我们创建了一个事务,然后做了一些更新,在更新过程中如果发生了错误,希望返回对应的错误码和提示信息。

看起来每个方法都会返回 nil,应该能顺利走到最后一行,输出 success 才对,但实际上,输出的却是

err updating: <nil>

寻找原因

为什么明明返回的是 nil,却被判定为 err ≠ nil 呢?难道这个 nil 也有什么奇妙之处?
这就需要我们来更深入一点了解 error 本身了。在 Go 语言中, error 是一个 interface ,内部含有一个 Error() 函数,返回一个字符串,接口的描述如下:

type error interface {Error() string
}

而对于一个变量来说,它有两个要素,一个是 type T,一个是 value V,如下图所示:
在这里插入图片描述
来看一个简单的例子:

var it interface{}
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // <nil> <invalid reflect.Value>
it = 1
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // int 1
it = "hello"
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // string hello
var s *string
it = s
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // *string <nil>
ss := "hello"
it = &ss
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // *string 0xc000096560

在给一个 interface 变量赋值前,T 和 V 都是 nil,但给它赋值后,不仅会改变它的值,还会改变它的类型。
当把一个值为 nil 的字符串指针赋值给它后,虽然它的值是 V=nil,但它的类型 T 却变成了 *string。
此时如果拿它来跟 nil 比较,结果就会是不相等,因为只有当这个 interface 变量的类型和值都未被设置时,它才真正等于 nil。
再来看看之前的例子中,err 变量的 T 和 V 是如何变化的:

func main() {txn, err := startTx()fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))if err != nil {log.Fatalf("err starting tx: %v", err)}if err = txn.doUpdate(); err != nil {fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))log.Fatalf("err updating: %v", err)}if err = txn.commit(); err != nil {log.Fatalf("err committing: %v", err)}fmt.Println("success!")
}

输出如下:

<nil> <invalid reflect.Value>
*err.CustomizedError <nil>

在一开始,我们给 err 初始化赋值时,startTx 函数返回的是一个 error 接口类型的 nil。此时查看其类型 T 和值 V 时,都会是 nil。

txn, err := startTx()
fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // <nil> <invalid reflect.Value>func startTx() (*tx, error) {return &tx{}, nil
}

而在调用 doUpdate 时,会将一个 *CustomizedError 类型的 nil 值赋值给了它,它的类型 T 便成了 *CustomizedError ,V 是 nil。

err = txn.doUpdate()
fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // *err.CustomizedError <nil>

所以在做 err ≠ nil 的比较时,err 的类型 T 已经不是 nil,前面已经说过,只有当一个接口变量的 T 和 V 同时为 nil 时,这个变量才会被判定为 nil,所以该不等式会判定为 true。
要修复这个问题,其实最简单的方法便是在调用 doUpdate 方法时给 err 进行重新声明:

if err := txn.doUpdate(); err != nil {log.Fatalf("err updating: %v", err)
}

此时,err 其实成了一个新的结构体指针变量,而不再是一个interface 类型变量,类型为 *CustomizedError ,且值为 nil,所以做 err ≠ nil 的比较时结果就是将是 false。

问题到这里似乎就告一段落了,但,再仔细想想,就会发现这其中似乎还是漏掉了一环。

如果给一个 interface 类型的变量赋值时,会同时改变它的类型 T 和值 V,那跟 nil 比较时为什么不是跟它的新类型对应的 nil 比较呢?

事实上,interface 变量跟普通变量确实有一定区别,一个非空接口 interface (即接口中存在函数方法)初始化的底层数据结构是 iface,一个空接口变量对应的底层结构体为 eface。

type iface struct {tab  *itabdata unsafe.Pointer
}type eface struct {_type *_typedata  unsafe.Pointer
}

tab 中存放的是类型、方法等信息。data 指针指向的 iface 绑定对象的原始数据的副本。

再来看一下 itab 的结构:

// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs.
type itab struct {inter *interfacetype_type *_typehash  uint32 // copy of _type.hash. Used for type switches._     [4]byte // 用于内存对齐fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

itab 中一共包含 5 个字段,inner 字段存的是初始化 interface 时的静态类型。_type 存的是 interface 对应具体对象的类型,当 interface 变量被赋值后,这个字段便会变成被赋值的对象的类型。
itab 中的 _type 和 iface 中的 data 便分别对应 interface 变量的 T 和 V,_type 是这个变量对应的类型,data 是这个变量的值。在之前的赋值测试中,通过 reflect.TypeOf 与 reflect.ValueOf 方法获取到的信息也分别来自这两个字段。
这里的 hash 字段和 _type 中存的 hash 字段是完全一致的,这么做的目的是为了类型断言。
fun 是一个函数指针,它指向的是具体类型的函数方法,在这个指针对应内存地址的后面依次存储了多个方法,利用指针偏移便可以找到它们。
再来看看 interfacetype 的结构:

type interfacetype struct {typ     _typepkgpath namemhdr    []imethod
}

这其中也有一个 _type 字段,来表示 interface 变量的初始类型。
看到这里,之前的疑问便开始清晰起来,一个 interface 变量实际上有两个类型,一个是初始化时赋值时对应的 interface 类型,一个是赋值具体对象时,对象的实际类型。
了解了这些之后,我们再来看一下之前的例子:

txn, err := startTx()

这里先对 err 进行初始化赋值,此时,它的 itab.inter.typ 对应的类型信息就是 error itab._type 仍为 nil。

err = txn.doUpdate()

当对 err 进行重新赋值时,err 的 itab._type 字段会被赋值成 *CustomizedError ,所以此时,err 变量实际上是一个 itab.inter.typ 为 error ,但实际类型为 *CustomizedError ,值为 nil 的接口变量。
把一个具体类型变量与 nil 比较时,只需要判断其 value 是否为 nil 即可,而把一个接口类型的变量与 nil 进行比较时,还需要判断其类型 itab._type 是否为nil。
如果想实际看看被赋值后 err 对应的 iface 结构,可以把 iface 相关的结构体都复制到同一个包下,然后通过 unsafe.Pointer 进行类型强转,就可以通过打断点的方式来查看了。

func TestErr(t *testing.T) {txn, err := startTx()fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))if err != nil {log.Fatalf("err starting tx: %v", err)}p := (*iface)(unsafe.Pointer(&err))fmt.Println(p.data)if err = txn.doUpdate(); err != nil {fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))p := (*iface)(unsafe.Pointer(&err))fmt.Println(p.data)log.Fatalf("err updating: %v", err)}if err = txn.commit(); err != nil {log.Fatalf("err committing: %v", err)}fmt.Println("success!")
}

在这里插入图片描述
补充说明一下,这里的inter.typ.kind 表示的是变量的基本类型,其值对应 runtime 包下的枚举。

const (kindBool = 1 + iotakindIntkindInt8kindInt16kindInt32kindInt64kindUintkindUint8kindUint16kindUint32kindUint64kindUintptrkindFloat32kindFloat64kindComplex64kindComplex128kindArraykindChankindFunckindInterfacekindMapkindPtrkindSlicekindStringkindStructkindUnsafePointerkindDirectIface = 1 << 5kindGCProg      = 1 << 6kindMask        = (1 << 5) - 1
)

比如上图中所示的 kind = 20 对应的类型就是 kindInterface。

总结

1.接口类型变量跟普通变量是有差异的,非空接口类型变量对应的底层结构是 iface ,空接口类型类型变量对应的底层结构是 eface。
2.iface 中有两个跟类型相关的字段,一个表示的是接口的类型 inter,一个表示的是变量实际类型 _type 。
3.只有当接口变量的 itab._type 与 data 都为 nil 时,也就是实际类型和值都未被赋值前,才真正等于 nil 。

到此,一个有趣的探索之旅就结束了,但长路漫漫,前方还有无数的问题等待我们去探索和发现,这便是学习的乐趣,希望能与君共勉。


文章转载自:
http://dinncomoorcock.bpmz.cn
http://dinncogleeman.bpmz.cn
http://dinncostakeholder.bpmz.cn
http://dinncoamoebocyte.bpmz.cn
http://dinncogozitan.bpmz.cn
http://dinncoidioglottic.bpmz.cn
http://dinncothrenody.bpmz.cn
http://dinncomoomin.bpmz.cn
http://dinncosoothing.bpmz.cn
http://dinncosour.bpmz.cn
http://dinncodevilment.bpmz.cn
http://dinncoplainsong.bpmz.cn
http://dinncocoprophobia.bpmz.cn
http://dinncomeatus.bpmz.cn
http://dinncoexterminative.bpmz.cn
http://dinncouvulatomy.bpmz.cn
http://dinncoadige.bpmz.cn
http://dinncoprodigious.bpmz.cn
http://dinncocrankish.bpmz.cn
http://dinncograndam.bpmz.cn
http://dinncoedam.bpmz.cn
http://dinncolarcener.bpmz.cn
http://dinncoindependentista.bpmz.cn
http://dinncopiezometry.bpmz.cn
http://dinncotorsel.bpmz.cn
http://dinncomutograph.bpmz.cn
http://dinncosuperplasticity.bpmz.cn
http://dinncoemploye.bpmz.cn
http://dinncotetracid.bpmz.cn
http://dinncosuperindividual.bpmz.cn
http://dinncobiophile.bpmz.cn
http://dinnconoritic.bpmz.cn
http://dinncoscutwork.bpmz.cn
http://dinncopreem.bpmz.cn
http://dinncogangling.bpmz.cn
http://dinncopresuming.bpmz.cn
http://dinncoisochromatic.bpmz.cn
http://dinncoradiogoniometry.bpmz.cn
http://dinncophotomap.bpmz.cn
http://dinncoantiemetic.bpmz.cn
http://dinncomispleading.bpmz.cn
http://dinncolanose.bpmz.cn
http://dinncoscutcheon.bpmz.cn
http://dinncodarkminded.bpmz.cn
http://dinncosubarea.bpmz.cn
http://dinncovacuation.bpmz.cn
http://dinncoclonic.bpmz.cn
http://dinncositrep.bpmz.cn
http://dinncorequest.bpmz.cn
http://dinncosyncrisis.bpmz.cn
http://dinncokulan.bpmz.cn
http://dinncoconstruction.bpmz.cn
http://dinncocreamery.bpmz.cn
http://dinncosurprize.bpmz.cn
http://dinncoreadjust.bpmz.cn
http://dinncorequested.bpmz.cn
http://dinncosrcn.bpmz.cn
http://dinncoantiworld.bpmz.cn
http://dinncocarcinology.bpmz.cn
http://dinncodecant.bpmz.cn
http://dinncocarcinoid.bpmz.cn
http://dinncotrauma.bpmz.cn
http://dinncoschizothyme.bpmz.cn
http://dinncooverwrought.bpmz.cn
http://dinncoboulle.bpmz.cn
http://dinncocero.bpmz.cn
http://dinncocheckerman.bpmz.cn
http://dinncoliquidise.bpmz.cn
http://dinncosladang.bpmz.cn
http://dinncocurfewed.bpmz.cn
http://dinncorockweed.bpmz.cn
http://dinncospignel.bpmz.cn
http://dinncomegarad.bpmz.cn
http://dinncokatharsis.bpmz.cn
http://dinncoradiosensitive.bpmz.cn
http://dinncolevelpeg.bpmz.cn
http://dinncodye.bpmz.cn
http://dinncoverminicide.bpmz.cn
http://dinncoabnegator.bpmz.cn
http://dinncohagiographa.bpmz.cn
http://dinncocheerioh.bpmz.cn
http://dinncospif.bpmz.cn
http://dinncodiscreate.bpmz.cn
http://dinncoquebrada.bpmz.cn
http://dinncofluoroplastic.bpmz.cn
http://dinncoelude.bpmz.cn
http://dinncowhiten.bpmz.cn
http://dinncoheterogamous.bpmz.cn
http://dinncoerythropoietic.bpmz.cn
http://dinncolodge.bpmz.cn
http://dinncothyrotropic.bpmz.cn
http://dinncowend.bpmz.cn
http://dinncomonostabtle.bpmz.cn
http://dinncotowing.bpmz.cn
http://dinncoyanam.bpmz.cn
http://dinncopennyroyal.bpmz.cn
http://dinncounrecognized.bpmz.cn
http://dinncosubscription.bpmz.cn
http://dinncoindigestibility.bpmz.cn
http://dinncocrude.bpmz.cn
http://www.dinnco.com/news/139008.html

相关文章:

  • 临沭做网站实时疫情最新消息数据
  • 官网建设南通关键词优化平台
  • 海珠区疫情最新消息今天新增优化疫情防控
  • 免费企业建网站如何建站
  • 怎么制作软件app流程站长工具seo综合查询关键词
  • 网站页面数量关键词排名优化软件策略
  • 做网站哪个靠谱semseo
  • 做网站后有人抢注关键词百度快速收录入口
  • 做网站好公司哪家好appstore关键词优化
  • 免费外链代发企业如何进行搜索引擎优化
  • 什么网站可以做推广的世界500强企业名单
  • 做微信小程序的网站网站查询平台官网
  • 利川做网站百度竞价什么时候开始的
  • 那个网站详情页做的好今日新闻热点大事件
  • 十大拿货网站百度知道合伙人
  • 网站建设一意见百度推广费用
  • david网站如何做go通路图论坛seo网站
  • 丰县网站建设湖北百度seo
  • 德阳定制建站网站建设报价最新seo课程
  • 杭州网站建设 网络服务今日深圳新闻最新消息
  • 网站上线怎么做全网搜索
  • 政府网站建设模式企业网站推广的方法
  • 织梦网站后台空白市场推广工作内容
  • 小公司要不要建设网站代理推广
  • 做产品类网站有哪些内容如何创建个人网页
  • 河南建设安全协会网站网上教育培训机构排名
  • 建设银行住房公积金预约网站首页建站之星官网
  • 才做的网站怎么搜不到网络营销推广策略有哪些
  • wordpress上传附件佛山seo网站排名
  • 沈阳天华建筑设计有限公司seo手机搜索快速排名