类型没有字段或方法读取(但它有)
Type has not field or method Read (but it does)
我被这个难住了。在我正在从事的项目中,我们从 Thrift 生成 go 代码。代码是在 A/B/thriftapi 包中创建的(以前是 A/B/thrift,这导致了问题,因为所有生成的代码都导入了 git.apache.org/thrift.git/lib/go/thrift
并导致名称冲突)。
我生成了代码并将代码移动到 $GOPATH/src/A/B/D
然后我尝试构建我的项目,但出现了大量以下形式的错误:
p.X.Read undefined (type Foo has no field or method Read)
我查看了其中一条违规行:
import (
"A/B/D"
"git.apache.org/thrift.git/lib/go/thrift"
)
func(p *Bar) readField1(iprot thrift.TProtocol) error {
p.X = D.NewFoo()
if err := p.X.Read(iprot); err != nil {
...
}
因为我使用的是 IntelliJ,所以我按 CTRL+单击 Read()
方法,果然它跳转到 $GOPATH/A/B/D/ttypes.go
方法
func (p *Foo) Read(iprot thrift.TProtocol) error {
...
}
这正是我希望方法所在的文件,它是指向 Foo
的指针上的方法,所以没有问题。一切似乎都应该是正确的,但在 IntelliJ 和命令行中我都遇到了这些问题。
知道可能出了什么问题吗?当它告诉我该方法不存在时令人沮丧,但如果我单击它(并且还会在智能感知中弹出),我会直接使用它
编辑 - 根据评论
type Bar struct {
X Foo `thrift:"x,1,required"`
}
请不要投反对票,但我想表达一个简单的代码来重现您可能遇到的问题。 (我没有使用 Thrift 的经验,但我认为它与包有关)
package main
import (
"fmt"
"D"
)
type Foo struct {}
func (f *Foo) PrintIt() {
fmt.Println("Sample printing")
}
type Bar struct {
// For the sake of this experiment
X *D.Foo
}
func (b *Bar) PrintFromBar() {
// simulates b.x = D.NewFoo()
b.X = new(D.Foo)
b.X.PrintIt() // The culprit happens here
}
func main() {
b := new(Bar)
b.PrintFromBar()
}
D包:
package D
type Foo struct {}
b.PrintFromBar()
失败并显示“b.X.PrintIt 未定义(类型 *D.Foo 没有字段或方法 PrintIt)。
问题可能在于 D.NewFoo()
为 *D.Foo
创建了一个名为 Foo
的别名。在你的情况下,因为你的 Read()
方法已经在 D 包中,所以我不知道没有完整的代码。然而,有趣的是,这实际上产生了同样的错误。
这是您所看到内容的最小复制。
package main
type A struct{}
type B *A
func (a *A) Read() {}
func main() {
var b B
b.Read()
}
编译产生此错误消息:
prog.go:11: b.Read undefined (type B has no field or method Read)
问题是 thrift 正在定义它自己的 Foo
,即 *D.Foo
,那是 b.X
的类型。 D.Foo
类型在我的代码中用A
表示,thrift引入的Foo
类型用B
表示。虽然 *D.Foo
有一个 Read()
方法,但 Foo
别名没有。这就是您看到错误消息的原因。在您的情况下,错误消息令人困惑,因为 Foo
模棱两可地引用 D.Foo
或错误文本中的 thrift 别名 - 编译器表示其中之一,而您将其解释为表示其他.
您可以通过编写 (*D.Foo)(b.X).Read()
或在复制案例中将值转换为正确的类型来解决别名问题:
package main
type A struct{}
type B *A
func (a *A) Read() {}
func main() {
var b B
(*A)(b).Read()
}
正如@Anyonymous 指出的那样,这是节俭别名和使用错误别名的问题。我认为这是 Thrift 编译器(在 0.9.2 和当前 HEAD 中)中的一个错误,因为它将生成永远无法运行的代码。我们还没有 运行 解决其他语言的这个问题,去吧。这是重现问题的简化:
// Base.thrift
namespace go a.X.c
struct Foo {
1: required string s
}
和相关文件
// Child.thrift
namespace go a.Y.c
include "Base.thrift"
typedef Base.Foo Foo // <---- This is what causes the problem
struct Bar {
1:Foo f // <-- Will error
// 1:Base.Foo f Need to comment out typedef and use this instead
}
按原样编译 thrift 没问题,但是当你去安装 a.Y.c
包时会产生:
/scratch/go/src/a/Y/c/ttypes.go:78: cannot use c.Foo literal (type *c.Foo) as type *Foo in assignment
/scratch/go/src/a/Y/c/ttypes.go:79: p.F.Read undefined (type *Foo has no field or method Read)
/scratch/go/src/a/Y/c/ttypes.go:105: p.F.Write undefined (type *Foo has no field or method Write)
如果我注释掉 typedef 并交换 Bar
中的行,那么一切正常。这似乎只发生在 Go 中。
我被这个难住了。在我正在从事的项目中,我们从 Thrift 生成 go 代码。代码是在 A/B/thriftapi 包中创建的(以前是 A/B/thrift,这导致了问题,因为所有生成的代码都导入了 git.apache.org/thrift.git/lib/go/thrift
并导致名称冲突)。
我生成了代码并将代码移动到 $GOPATH/src/A/B/D
然后我尝试构建我的项目,但出现了大量以下形式的错误:
p.X.Read undefined (type Foo has no field or method Read)
我查看了其中一条违规行:
import (
"A/B/D"
"git.apache.org/thrift.git/lib/go/thrift"
)
func(p *Bar) readField1(iprot thrift.TProtocol) error {
p.X = D.NewFoo()
if err := p.X.Read(iprot); err != nil {
...
}
因为我使用的是 IntelliJ,所以我按 CTRL+单击 Read()
方法,果然它跳转到 $GOPATH/A/B/D/ttypes.go
方法
func (p *Foo) Read(iprot thrift.TProtocol) error {
...
}
这正是我希望方法所在的文件,它是指向 Foo
的指针上的方法,所以没有问题。一切似乎都应该是正确的,但在 IntelliJ 和命令行中我都遇到了这些问题。
知道可能出了什么问题吗?当它告诉我该方法不存在时令人沮丧,但如果我单击它(并且还会在智能感知中弹出),我会直接使用它
编辑 - 根据评论
type Bar struct {
X Foo `thrift:"x,1,required"`
}
请不要投反对票,但我想表达一个简单的代码来重现您可能遇到的问题。 (我没有使用 Thrift 的经验,但我认为它与包有关)
package main
import (
"fmt"
"D"
)
type Foo struct {}
func (f *Foo) PrintIt() {
fmt.Println("Sample printing")
}
type Bar struct {
// For the sake of this experiment
X *D.Foo
}
func (b *Bar) PrintFromBar() {
// simulates b.x = D.NewFoo()
b.X = new(D.Foo)
b.X.PrintIt() // The culprit happens here
}
func main() {
b := new(Bar)
b.PrintFromBar()
}
D包:
package D
type Foo struct {}
b.PrintFromBar()
失败并显示“b.X.PrintIt 未定义(类型 *D.Foo 没有字段或方法 PrintIt)。
问题可能在于 D.NewFoo()
为 *D.Foo
创建了一个名为 Foo
的别名。在你的情况下,因为你的 Read()
方法已经在 D 包中,所以我不知道没有完整的代码。然而,有趣的是,这实际上产生了同样的错误。
这是您所看到内容的最小复制。
package main
type A struct{}
type B *A
func (a *A) Read() {}
func main() {
var b B
b.Read()
}
编译产生此错误消息:
prog.go:11: b.Read undefined (type B has no field or method Read)
问题是 thrift 正在定义它自己的 Foo
,即 *D.Foo
,那是 b.X
的类型。 D.Foo
类型在我的代码中用A
表示,thrift引入的Foo
类型用B
表示。虽然 *D.Foo
有一个 Read()
方法,但 Foo
别名没有。这就是您看到错误消息的原因。在您的情况下,错误消息令人困惑,因为 Foo
模棱两可地引用 D.Foo
或错误文本中的 thrift 别名 - 编译器表示其中之一,而您将其解释为表示其他.
您可以通过编写 (*D.Foo)(b.X).Read()
或在复制案例中将值转换为正确的类型来解决别名问题:
package main
type A struct{}
type B *A
func (a *A) Read() {}
func main() {
var b B
(*A)(b).Read()
}
正如@Anyonymous 指出的那样,这是节俭别名和使用错误别名的问题。我认为这是 Thrift 编译器(在 0.9.2 和当前 HEAD 中)中的一个错误,因为它将生成永远无法运行的代码。我们还没有 运行 解决其他语言的这个问题,去吧。这是重现问题的简化:
// Base.thrift
namespace go a.X.c
struct Foo {
1: required string s
}
和相关文件
// Child.thrift
namespace go a.Y.c
include "Base.thrift"
typedef Base.Foo Foo // <---- This is what causes the problem
struct Bar {
1:Foo f // <-- Will error
// 1:Base.Foo f Need to comment out typedef and use this instead
}
按原样编译 thrift 没问题,但是当你去安装 a.Y.c
包时会产生:
/scratch/go/src/a/Y/c/ttypes.go:78: cannot use c.Foo literal (type *c.Foo) as type *Foo in assignment
/scratch/go/src/a/Y/c/ttypes.go:79: p.F.Read undefined (type *Foo has no field or method Read)
/scratch/go/src/a/Y/c/ttypes.go:105: p.F.Write undefined (type *Foo has no field or method Write)
如果我注释掉 typedef 并交换 Bar
中的行,那么一切正常。这似乎只发生在 Go 中。