恐慌:接口转换:*不是接口X:缺少方法xxx

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

Panic: interface conversion: * is not inteface X: missing method xxx

问题

我正在为一个项目工作,该项目从Kafka消费消息,使用MessagePack或JSON格式进行解包,构建SQL并将其插入TDengine数据库。

接口定义如下:

  1. type TaosEncoder interface {
  2. TaosDatabase() string
  3. TaosSTable() string
  4. TaosTable() string
  5. TaosTags() []interface{}
  6. TaosCols() []string
  7. TaosValues() []interface{}
  8. }

对于一个名为Record的对象:

  1. type Record struct {
  2. DeviceID string `json:"deviceID"` // 用于表名
  3. Timestamp time.Time `json:"timestamp"`
  4. Cols []interface{} `json:"cols"`
  5. }

实现该接口:

  1. func (r *Record) TaosCols() []string {
  2. var tags []string
  3. return tags
  4. }
  5. // 其他方法也要实现

一个方法将使用接口方法:

  1. func ToTaosBatchInsertSql(l []interface{}) (string, error) {
  2. records := make([]string, len(l))
  3. for i, t := range l {
  4. // type TaosEncoderT = *TaosEncoder
  5. record, err := ToTaosRecordSql(t.(TaosEncoder))
  6. if err != nil {
  7. return "", err
  8. }
  9. records[i] = record
  10. }
  11. return fmt.Sprintf("insert into %s", strings.Join(records, " ")), nil
  12. }

使用方法如下:

  1. records := make([]interface{}, size)
  2. for i := 0; i < size; i++ {
  3. item := <-q.queue // 一个Record类型的通道,定义为:queue chan interface{}
  4. records[i] = item
  5. }
  6. sqlStr, err := utils.ToTaosBatchInsertSql(records)

它可以编译通过,但在运行时失败,如下所示:

  1. panic: interface conversion: record.Record is not utils.TaosEncoder: missing method TaosCols
  2. goroutine 71 [running]:
  3. xxx/pkg/utils.ToTaosBatchInsertSql(0xc000130c80, 0xc8, 0xc8, 0xc8, 0x0, 0x0, 0x0)

但是,当我更改Record的实现 - 移除*时:

  1. func (r Record) TaosCols() []string {
  2. var tags []string
  3. return tags
  4. }

然后它就可以正常工作了。

我被告知在接口实现中使用*,所以问题是:

  1. 使用func (r Record) xxx实现接口是否可以?
  2. 如果不行,我该怎么办?
英文:

I'm working on a project that consume messages from Kafka, unmushal using messagepack or json format, build sql of it and insert them into TDengine database.

The inteface defined as:

  1. type TaosEncoder interface {
  2. TaosDatabase() string
  3. TaosSTable() string
  4. TaosTable() string
  5. TaosTags() []interface{}
  6. TaosCols() []string
  7. TaosValues() []interface{}
  8. }

For an object called Record:

  1. type Record struct {
  2. DeviceID string `json:&quot;deviceID&quot;` // for table name
  3. Timestamp time.Time `json:&quot;timestamp&quot;`
  4. Cols []interface{} `json:&quot;cols&quot;`
  5. }

Implement the interface:

  1. func (r *Record) TaosCols() []string {
  2. var tags []string
  3. return tags
  4. }
  5. // other methods are implemented too

A method will use the interface methods:

  1. func ToTaosBatchInsertSql(l []interface{}) (string, error) {
  2. records := make([]string, len(l))
  3. for i, t := range l {
  4. // type TaosEncoderT = *TaosEncoder
  5. record, err := ToTaosRecordSql(t.(TaosEncoder))
  6. if err != nil {
  7. return &quot;&quot;, err
  8. }
  9. records[i] = record
  10. }
  11. return fmt.Sprintf(&quot;insert into %s&quot;, strings.Join(records, &quot; &quot;)), nil
  12. }

Use it as this:

  1. records := make([]interface{}, size)
  2. for i := 0; i &lt; size; i++ {
  3. item := &lt;-q.queue # an channel of Record, defined as: queue chan interface{}
  4. records[i] = item
  5. }
  6. sqlStr, err := utils.ToTaosBatchInsertSql(records)

It compiles ok, but failed while running like this:

  1. panic: interface conversion: record.Record is not utils.TaosEncoder: missing method TaosCols
  2. goroutine 71 [running]:
  3. xxx/pkg/utils.ToTaosBatchInsertSql(0xc000130c80, 0xc8, 0xc8, 0xc8, 0x0, 0x0, 0x0)

But when I change the implementations of Record - remove *

  1. func (r Record) TaosCols() []string {
  2. var tags []string
  3. return tags
  4. }

Then it just works.

I've told to use * in interface implements, so the question is:

  1. Is it ok to implement interface with func (r Record) xxx?
  2. If not, what can I do?

答案1

得分: 2

这个错误的原因与类型的方法集有关。

假设有一个类型 T,它有一些接收者类型为 T 的方法,还有一些接收者类型为 *T 的方法。

那么,如果一个变量的类型是 T,它可以使用所有接收者类型为 T 的方法,但不能使用接收者类型为 *T 的方法

如果一个变量的类型是 *T,它可以使用两组方法,一组是接收者类型为 T 的方法,另一组是接收者类型为 *T 的方法。

在你的情况下,类型 Record 没有 TaosCols() 方法,因此它没有完全实现接口 TaosEncoder

如果将类型为 Record 的变量转换为 *Record 类型的变量,它就可以使用所有的方法,并且可以实现该接口。

Method Sets in: Frequently Asked Questions (FAQ) - The Go Programming Language

Method Sets in: The Go Programming Language Specification - The Go Programming Language

英文:

The cause of this error is related to the method sets of a type.

Assume a type T that has methods with a receiver type of T and some more methods with a receiver type of *T.

Then, if a variable is of type T, it has all methods available that have a pointer receiver of T but none of the methods with receiver type *T.

If a variable is of type *T, it has both sets of methods available, those with receiver type T, and those with receiver type *T.

In your case, type Record has no TaosCols() method, and so it does not implement interface TaosEncoder completely.

If you turn the variable of type Record into a *Record variable, it has all methods available and then would implement the interface.

Method Sets in: Frequently Asked Questions (FAQ) - The Go Programming Language

Method Sets in: The Go Programming Language Specification - The Go Programming Language

答案2

得分: 0

一个 Record 类型和 *Record 类型不同。如果你使用指针接收器来定义实现接口的方法,那么只有 *Record 类型才实现了 TaosEncoder 接口。

根据这个代码片段:

  1. item := <-q.queue // 一个 Record 类型的通道

看起来你需要发送一个 *Record 类型的值到 q.queue,而不是一个 Record 类型的值。

英文:

A Record is not the same type as a *Record. If you use a pointer receiver for defining the methods that implement the interface, then only *Record implements TaosEncoder.

Based on this:

  1. item := &lt;-q.queue # an channel of Record

it looks like you will need to send a value of *Record on q.queue, rather than a Record.

huangapple
  • 本文由 发表于 2021年7月16日 18:08:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/68407216.html
匿名

发表评论

匿名网友

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

确定