Type没有Read字段或方法(但它确实有)

huangapple go评论88阅读模式
英文:

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"`    
}
英文:

I'm stumped on this one. In the project that I'm working on, we generate go code from Thrift. The code gets created in the package A/B/thriftapi (which used to be A/B/thrift which was causing problems because all of the generated code was importing git.apache.org/thrift.git/lib/go/thrift and causing name collisions).

I generated the code and moved the code into $GOPATH/src/A/B/D I then tried to build my project and was getting tons of errors of the form:

p.X.Read undefined (type Foo has no field or method Read)

I looked at one of the offending lines:

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 { 
    ...
 }

Since I am using IntelliJ, I CTRL+clicked on the Read() method and sure enough it jumps to $GOPATH/A/B/D/ttypes.go to the method

func (p *Foo) Read(iprot thrift.TProtocol) error {
    ...
}

That's exactly the file I'd expect the method to be in, and it's a method on a pointer to Foo so no problems there. Everything seems like it should be right, but both in IntelliJ and on the command line I get these problems.

Any ideas what might be going wrong? It's frustrating when it tells me the method doesn't exist, yet will take me right to it if I click on it (and also pops up in the intellisense)

EDIT - Per comment

type Bar struct {
   X Foo `thrift:"x,1,required"`    
}

答案1

得分: 12

这是你所看到的问题的最小复现代码:

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()
}
英文:

Here's a minimal reproduction of what you're seeing.

package main

type A struct{}

type B *A

func (a *A) Read() {}

func main() {
	var b B
	b.Read()
}

Compiling produces this error message:

prog.go:11: b.Read undefined (type B has no field or method Read)

The problem is that thrift is defining it's own Foo which is *D.Foo, and that's the type of b.X. The D.Foo type is represented by A in my code, and the Foo type that thrift introduces is represented by B. Although *D.Foo has a Read() method, the Foo alias doesn't. That's why you're seeing the error message. The error message is confusing in your case because Foo is ambiguously referring to D.Foo or the thrift alias in the text of the error — the compiler means one of them, and you're interpreting it to mean the other.

You can work through the alias by converting the value to the right type by writing (*D.Foo)(b.X).Read() or in the reproduction case:

package main

type A struct{}

type B *A

func (a *A) Read() {}

func main() {
	var b B
	(*A)(b).Read()
}

答案2

得分: 2

请不要投票反对,但我想表达一个简单的代码来重现你可能遇到的情况。(我对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 undefined (type *D.Foo has no field or method PrintIt)。

问题可能在于 D.NewFoo()*D.Foo 创建了一个名为 Foo 的别名。在你的情况下,由于你的 Read() 方法已经在 D 包内部,我没有完整的代码,所以我不知道具体情况。然而,有趣的是,这实际上产生了相同的错误。

英文:

Please do not vote down, but I want to express a simple code to reproduce what you might have encountered. (I have no experience with Thrift, but I think it has more to do with packages)

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()
}

The D package:

package D

type Foo struct {}

b.PrintFromBar() fails with "b.X.PrintIt undefined (type *D.Foo has no field or method PrintIt).

The problem might lies in D.NewFoo() creating an alias named Foo for *D.Foo. In your case since your Read() method is already inside the D package, I have no idea without the full code. However, it's interesting that this actually produce the same error.

答案3

得分: 1

正如@Anyonymous指出的,这是关于Thrift别名和使用错误别名的问题。我认为这是Thrift编译器的一个错误(在0.9.2和当前的HEAD版本中),它会生成永远无法工作的代码。我们在其他语言中没有遇到这个问题,只有在Go中。以下是一个简化的示例来重现这个问题:

// 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 // <---- 这是引起问题的地方

struct Bar {
1:Foo f // <-- 会出错
// 1:Base.Foo f 需要注释掉typedef并使用这个代替
}

按原样编译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中。

英文:

As @Anyonymous points out, this is a problem with thrift aliasing and using the wrong one. I consider this a bug in the Thrift compiler (in 0.9.2 and current HEAD) in that it will generate code that will never work. We haven't run into this problem with other languages, just go. Here is a simplification to reproduce the problem:

// Base.thrift
namespace go a.X.c
struct Foo {
    1: required string s
}

and the depenant file

// Child.thrift
namespace go a.Y.c
include &quot;Base.thrift&quot;

typedef Base.Foo Foo // &lt;---- This is what causes the problem

struct Bar {
    1:Foo f  // &lt;-- Will error
    // 1:Base.Foo f   Need to comment out typedef and use this instead
}

Compiling the thrift as is will be fine, but when you go to install the a.Y.c package will produce:

/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)

If I comment out the typedef and swap the lines in Bar then everything works fine. This appears to only happen in Go.

huangapple
  • 本文由 发表于 2015年3月7日 05:27:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/28907976.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定