Type has not field or method Read (but it does)

FuriousGeorge picture FuriousGeorge · Mar 6, 2015 · Viewed 13.4k times · Source

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"`    
}

Answer

Paul Hankin picture Paul Hankin · Mar 8, 2015

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