英文:
Panic: interface conversion: * is not inteface X: missing method xxx
问题
我正在为一个项目工作,该项目从Kafka消费消息,使用MessagePack或JSON格式进行解包,构建SQL并将其插入TDengine数据库。
接口定义如下:
type TaosEncoder interface {
	TaosDatabase() string
	TaosSTable() string
	TaosTable() string
	TaosTags() []interface{}
	TaosCols() []string
	TaosValues() []interface{}
}
对于一个名为Record的对象:
type Record struct {
	DeviceID  string        `json:"deviceID"` // 用于表名
	Timestamp time.Time     `json:"timestamp"`
	Cols      []interface{} `json:"cols"`
}
实现该接口:
func (r *Record) TaosCols() []string {
	var tags []string
	return tags
}
// 其他方法也要实现
一个方法将使用接口方法:
func ToTaosBatchInsertSql(l []interface{}) (string, error) {
	records := make([]string, len(l))
	for i, t := range l {
		// type TaosEncoderT = *TaosEncoder
		record, err := ToTaosRecordSql(t.(TaosEncoder))
		if err != nil {
			return "", err
		}
		records[i] = record
	}
	return fmt.Sprintf("insert into %s", strings.Join(records, " ")), nil
}
使用方法如下:
records := make([]interface{}, size)
for i := 0; i < size; i++ {
	item := <-q.queue // 一个Record类型的通道,定义为:queue chan interface{}
	records[i] = item
}
sqlStr, err := utils.ToTaosBatchInsertSql(records)
它可以编译通过,但在运行时失败,如下所示:
panic: interface conversion: record.Record is not utils.TaosEncoder: missing method TaosCols
goroutine 71 [running]:
xxx/pkg/utils.ToTaosBatchInsertSql(0xc000130c80, 0xc8, 0xc8, 0xc8, 0x0, 0x0, 0x0)
但是,当我更改Record的实现 - 移除*时:
func (r Record) TaosCols() []string {
	var tags []string
	return tags
}
然后它就可以正常工作了。
我被告知在接口实现中使用*,所以问题是:
- 使用
func (r Record) xxx实现接口是否可以? - 如果不行,我该怎么办?
 
英文:
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:
type TaosEncoder interface {
	TaosDatabase() string
	TaosSTable() string
	TaosTable() string
	TaosTags() []interface{}
	TaosCols() []string
	TaosValues() []interface{}
}
For an object called Record:
type Record struct {
	DeviceID  string        `json:"deviceID"` // for table name
	Timestamp time.Time     `json:"timestamp"`
	Cols      []interface{} `json:"cols"`
}
Implement the interface:
func (r *Record) TaosCols() []string {
	var tags []string
	return tags
}
// other methods are implemented too
A method will use the interface methods:
func ToTaosBatchInsertSql(l []interface{}) (string, error) {
	records := make([]string, len(l))
	for i, t := range l {
		// type TaosEncoderT = *TaosEncoder
		record, err := ToTaosRecordSql(t.(TaosEncoder))
		if err != nil {
			return "", err
		}
		records[i] = record
	}
	return fmt.Sprintf("insert into %s", strings.Join(records, " ")), nil
}
Use it as this:
records := make([]interface{}, size)
for i := 0; i < size; i++ {
	item := <-q.queue # an channel of Record, defined as: queue chan interface{}
	records[i] = item
}
sqlStr, err := utils.ToTaosBatchInsertSql(records)
It compiles ok, but failed while running like this:
panic: interface conversion: record.Record is not utils.TaosEncoder: missing method TaosCols
goroutine 71 [running]:
xxx/pkg/utils.ToTaosBatchInsertSql(0xc000130c80, 0xc8, 0xc8, 0xc8, 0x0, 0x0, 0x0)
But when I change the implementations of Record - remove *
func (r Record) TaosCols() []string {
	var tags []string
	return tags
}
Then it just works.
I've told to use * in interface implements, so the question is:
- Is it ok to implement interface with 
func (r Record) xxx? - 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 接口。
根据这个代码片段:
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:
item := <-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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论