首页 > 解决方案 > go/types.Identical 不能比较其他包中的两个相同类型?

问题描述

我编写了一些代码以通过“go/types”获取特殊类型

import (
    "go/ast"
    "go/importer"
    "go/parser"
    "go/token"
    "go/types"
    "log"
)    

const (
    Src = `
package types

import (
    "io"
    "net/http"
)

var (
    IOReader    io.Reader
    Err         error
    StatusCode  int
    Request     *http.Request
    Response    *http.Response
)
`
)

const (
    TypeIOReader   = "IOReader"
    TypeErr        = "Err"
    TypeStatusCode = "StatusCode"
    TypeRequest    = "Request"
    TypeResponse   = "Response"
)


func GetType(name string) types.Type {
    fset := token.NewFileSet()
    file, err := parser.ParseFile(fset, "types.go", Src, 0)
    if err != nil {
        log.Fatal(err)
    }

    conf := types.Config{Importer: importer.Default()}
    pkg, err := conf.Check("impler/types", fset, []*ast.File{file}, nil)
    return pkg.Scope().Lookup(name).Type()
}

该功能GetType(name string) types.Type可以实现。

当我比较两种类型时,我得到了一些奇怪的结果

log.Println(types.Identical(GetType(TypeResponse), GetType(TypeResponse)))
log.Println(types.Identical(GetType(TypeIOReader), GetType(TypeIOReader)))
log.Println(types.Identical(GetType(TypeStatusCode), GetType(TypeStatusCode)))
log.Println(types.Identical(GetType(TypeErr), GetType(TypeErr)))

结果是

false
false
true
true

似乎types.Identical无法比较其他包中的两种相同类型?我该如何比较它们?

types.TypeString(typ1, nil) == types.TypeString(typ2, nil)健壮吗?

标签: go

解决方案


问题是您每次都重新解析源,创建一个新实例types.Package及其关联types.Scope,这导致命名类型源自不同的声明(链接)。

内置类型,如interror您获得true的是基本类型,通过比较(链接)区别对待。它们也在“宇宙”范围内声明,我相信,无论您重新解析多少次Src链接),它总是相同的。

要解决您的问题,您必须解析一次源代码,然后共享生成的*types.Package.

package main

import (
    "go/ast"
    "go/importer"
    "go/parser"
    "go/token"
    "go/types"
    "log"
)

const Src = `
package types

import (
    "io"
    "net/http"
)

var (
    IOReader    io.Reader
    Response    *http.Response
)
`


const (
    TypeIOReader = "IOReader"
    TypeResponse = "Response"
)

func GetType(name string, pkg *types.Package) types.Type {
    return pkg.Scope().Lookup(name).Type()
}

func main() {
    fset := token.NewFileSet()
    file, err := parser.ParseFile(fset, "types.go", Src, 0)
    if err != nil {
        panic(err)
    }

    conf := types.Config{Importer: importer.Default()}
    pkg, err := conf.Check("impler/types", fset, []*ast.File{file}, nil)
    if err != nil {
        panic(err)
    }

    log.Println(types.Identical(GetType(TypeResponse, pkg), GetType(TypeResponse, pkg)))
    log.Println(types.Identical(GetType(TypeIOReader, pkg), GetType(TypeIOReader, pkg)))
}

推荐阅读