英文:
How to add interface to non-local struct in golang?
问题
我使用https://github.com/lib/pq从Postgres获取数据。
为了提取数据,我使用了一个带有protobuf Timestamp字段的第三方结构体,链接在这里:https://pkg.go.dev/google.golang.org/protobuf/types/known/timestamppb#Timestamp
所以问题是如何将time.Time的值转换为timestamppb.Timestamp并使Scan函数正常工作。
我尝试了将值扫描到实现了sql.Scanner接口的结构体中,这很简单。我只需像这样实现Scan函数:
type Test struct {
}
func (t Test) Scan(src any) error {
// 转换值
}
但是对于timestamppb.Timestamp这种非本地类型,这种方法不起作用。
然后我尝试定义本地类型并使用相同的技巧:
type TimestampPb timestamppb.Timestamp
func (t TimestampPb) Scan(src any) error {
// 转换值
}
但是这个技巧也不起作用。而且我收到了警告:“'Scan'通过值传递锁:类型'TimestampPb'包含'protoimpl.MessageState'包含'sync.Mutex',它是'sync.Locker'”。
如果我为TimestampPb指定指针,这个技巧也不起作用:
func (t *TimestampPb) Scan(src any) error {
// 转换值
}
所以我想知道,如何使timestamppb.Timestamp成为sql.Scanner的实例。这可能吗?
更新1
我尝试了一个小例子:
type TimestampPb timestamppb.Timestamp
type test struct {
date *timestamppb.Timestamp
}
func (s *Storage) test() {
rows, _ := s.db.Query("SELECT \"test_date\" FROM \"test_table\" LIMIT 1")
defer func() {
if rows != nil {
_ = rows.Close()
}
}()
for rows.Next() {
//var value *TimestampPb //可以工作
//var value TimestampPb //可以工作
//var value *timestamppb.Timestamp //不起作用
//testStruct := test{} //不起作用
//err := rows.Scan(&value) //扫描到变量
//err := rows.Scan(&testStruct.date) //扫描到结构体中的字段
if err != nil {
log.Fatalln(err)
}
}
}
func (tpb *TimestampPb) Scan(src any) error {
return nil
}
我想知道是否可以使用testStruct的情况?
英文:
I use https://github.com/lib/pq for getting data from postgres.
For extracting data I use third-party struct, which has field with protobuf Timestamp https://pkg.go.dev/google.golang.org/protobuf/types/known/timestamppb#Timestamp
So
So the issue is make work Scan from time.Time to timestamppb.Timestamp
type Stuff struct { //this struct non-local, this is from third-party package
Date *timestamppb.Timestamp
}
stuff = Stuff{}
scanErr := rows.Scan(&stuff.Date)
I tried to Scan to struct witch implements sql.Scanner interface. That was easy. I just implement Scan function like this:
type Test struct {
}
func (t Test) Scan(src any) error {
//convert value
}
but it doesn't work with timestamppb.Timestamp because it is non-local type.
So then I tried to define local type and do the same trick
type TimestampPb timestamppb.Timestamp
func (t TimestampPb) Scan(src any) error {
//convert value
}
but this trick didn't work. Moreover I have warning "'Scan' passes a lock by the value: type 'TimestampPb' contains 'protoimpl.MessageState' contains 'sync.Mutex' which is 'sync.Locker'"
This either doesn't work if I specify pointer for TimestampPb
func (t *TimestampPb) Scan(src any) error {
So I wonder, how can I make timestamppb.Timestamp instance of sql.Scanner. Is it possible?
Update 1
Little example that I tried
type TimestampPb timestamppb.Timestamp
type test struct { //third-party struct, I can't modify it
date *timestamppb.Timestamp
}
func (s *Storage) test() {
rows, _ := s.db.Query("SELECT \"test_date\" FROM \"test_table\" LIMIT 1")
defer func() {
if rows != nil {
_ = rows.Close()
}
}()
for rows.Next() {
//var value *TimestampPb //works
//var value TimestampPb //works
//var value *timestamppb.Timestamp //doesn't work
//testStruct := test{} //doesn't work
//err := rows.Scan(&value) //scan to variable
//err := rows.Scan(&testStruct.date) //scan to field in the struct
if err != nil {
log.Fatalln(err)
}
}
}
func (tpb *TimestampPb) Scan(src any) error {
return nil
}
I wonder if I can use case with the testStruct?
答案1
得分: 0
pq
库不支持*timestamppb.Timestamp
作为时间戳类型。请参阅文档中支持的日期类型。
需要使用不同的类型进行扫描。
通常我会在函数中使用辅助类型来实现。例如:
type row struct {
Date *timestamppb.Timestamp
// ... 其他字段
}
func Query() ([]row, error) {
rows, err := s.db.Query(...)
if err != nil {
return nil, err
}
defer rows.Close()
var rows []row
for rows.Next() {
type aux struct {
Date time.Time
// ... 其他字段
}
var value aux
if err := rows.Scan(&value); err != nil {
return nil, err
}
rows = append(rows, row{
Date: timestamppb.New(value.Date),
// ... 其他字段
}
}
return rows, nil
}
希望这可以帮助到你!
英文:
The pq
library doesn't support *timestamppb.Timestamp
as a timestamp type. See the supported date types in the documentation.
A different type will need to be for scanning.
Often I do that by using an auxiliary type in the function. For example:
type row struct {
Date *timestamppb.Timestamp
// ... more fields here
}
func Query() ([]row, error) {
rows, err := s.db.Query(...)
if err != nil {
return nil, err
}
defer rows.Close()
var rows []row
for rows.Next() {
type aux struct {
Date time.Time
// ... more fields here
}
var value aux
if err := rows.Scan(&value); err != nil {
return nil, err
}
rows = append(rows, row{
Date: timestamppb.New(value.Date),
// ... more fields here
}
}
return rows, nil
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论