Stack Exchange
Repackaging C types causes type cast error in Go
I'm trying to repackage some go code that integrates with an existing C library.
The following works perfectly.
File 1:
package avcodec

type Codec C.struct_AVCodec

File 2:
package avformat

//#cgo pkg-config: libavformat libavcodec
//#include <libavformat/avformat.h>
//#include <libavcodec/avcodec.h>
import "C" 
import (    
"unsafe" 
) 

type Codec C.struct_AVCodec

func (s *FormatContext) AvFormatGetVideoCodec() *Codec {    
  result := C.av_format_get_video_codec((*C.struct_AVFormatContext)(s))
  return (*Codec)(result) // <- This works. Codec is defined in this package.
}

If I try to reference or move Codec from File 2 into a separate package (eg. File 1) I get the error: 
cannot convert (func literal)((*C.struct_AVFormatContext)(s)) (type *C.struct_AVCodec) to type *Codec
For example, this fails:
package avformat

//#cgo pkg-config: libavformat libavcodec
//#include <libavformat/avformat.h>
//#include <libavcodec/avcodec.h>
import "C" 
import (    
"avcodec"
"unsafe" 
) 

func (s *FormatContext) AvFormatGetVideoCodec() *avcodec.Codec {    
 result := C.av_format_get_video_codec((*C.struct_AVFormatContext)(s))      
 return (*avcodec.Codec)(result) // <- This fails. Codec defined in avcodec.
}

This also fails:
package avformat

//#cgo pkg-config: libavformat libavcodec
//#include <libavformat/avformat.h>
//#include <libavcodec/avcodec.h>    
import "C" 
import ( 
"avcodec"   
"unsafe" 
) 

type Codec avcodec.Codec

func (s *FormatContext) AvFormatGetVideoCodec() *Codec {    
  result := C.av_format_get_video_codec((*C.struct_AVFormatContext)(s))
  return (*Codec)(result) // <- This also fails. Codec is based on avcodec.Codec.
}

I'd like to:

Understand why it fails, and 
Identify how I can change the packaging so that the function uses the Codec type that is defined in the avcodec package.

Thanks in advance.    

There is a lot going on in that one line. I would suggest trying each piece by one to see which one fails.

The error is happening on the return. I've simplified the code to highlight the casting error. Everything else works :)

What is the definition of avcodec.Codec ? If you're defining a Go type, you only need to define it in once place. Types are not aliases, so I'm not sure what you're trying to do by defining Codec in each package.

@JimB Looks like that redefinition was just as an experiment.

Even with the type redefinitions I can't replicate the failure. What we need is a [mcve].

Thanks for taking a stab at this. I've found a workable solution. @JimB the problem pops-up when you are using C imports. Nonetheless, thanks for trying.

@haroldcampbell: yes, I understand the C types can differ slightly, especially when the C type is a typedef. You also should be able to convert these types if everything is declared correctly. I fail to see the benefit to the solution below at all though, since if you're gong to accept an interface{}, what's the point of defining the extra types at all?

@JimB: I use the Codec type elsewhere in the program, and do require the underlying C.struct_AVCodec for several methods. By using the type assertion, I'm in essence solving the problem of accepting and forcing a check that the underlying type is C.struct_AVCodec (eg. in the AvformatNewStream(...) method below. I'll agree that this solution with the interface{} is not prettiest, but I haven't been able to find a better solution.

@JimB: I've updated the question to make it adhere to Minimal, Complete, and Verifiable example. Hopefully you'll be able to reproduce the problem.

@haroldcampbell: I think I see what you're trying to do now. Yes, a C.struct_AVCodec defined in one package is a different type than a C.struct_AVCodec defined in another, so converting between them doesn't work. If you want to be able to covert that to an avcodec.Codec from within other packages, you need to use unsafe, or just use the C types directly throughout. The C type system is not the Go type system, and manual conversions are often necessary.

This was failing due to how Go represents types.
For instance, given:
//Ex1
package avformat 
//.. deleted for simplicity

type Codec C.struct_AVCodec

...and
//Ex2
package avcode 
//.. deleted for simplicity

type Codec C.struct_AVCodec

In the code above, the C.struct_AVCodec in ex1 is different from the C.struct_AVCodec in ex2, even though the same are lexically the same.
Specifically, the fully qualified type in ex1 is avformat._Ctype_struct_AVCodec, while ex2 is avcodec._Ctype_struct_AVCodec
This explains why the functions in the package avformat that were trying to cast anything from an external type (in this case from package avcodec) to the local C.struct_AVCodec were failing.
Solution
To get this to work, I relied on type assertions.
package avformat

func (s *FormatContext) AvformatNewStream(c avcodec.ICodec) *Stream {
  v, _ := c.(*C.struct_AVCodec)
  return (*Stream)(C.avformat_new_stream((*C.struct_AVFormatContext)(s), (*C.struct_AVCodec)(v)))
}

...and
package avcodec

type ICodec interface{}


Excellent answer about how things work in the innards of cgo. I'm definitely going to remember this gotcha.
