如何在Golang中为非本地结构添加接口?

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

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函数:

  1. type Test struct {
  2. }
  3. func (t Test) Scan(src any) error {
  4. // 转换值
  5. }

但是对于timestamppb.Timestamp这种非本地类型,这种方法不起作用。
然后我尝试定义本地类型并使用相同的技巧:

  1. type TimestampPb timestamppb.Timestamp
  2. func (t TimestampPb) Scan(src any) error {
  3. // 转换值
  4. }

但是这个技巧也不起作用。而且我收到了警告:“'Scan'通过值传递锁:类型'TimestampPb'包含'protoimpl.MessageState'包含'sync.Mutex',它是'sync.Locker'”。
如果我为TimestampPb指定指针,这个技巧也不起作用:

  1. func (t *TimestampPb) Scan(src any) error {
  2. // 转换值
  3. }

所以我想知道,如何使timestamppb.Timestamp成为sql.Scanner的实例。这可能吗?

更新1
我尝试了一个小例子:

  1. type TimestampPb timestamppb.Timestamp
  2. type test struct {
  3. date *timestamppb.Timestamp
  4. }
  5. func (s *Storage) test() {
  6. rows, _ := s.db.Query("SELECT \"test_date\" FROM \"test_table\" LIMIT 1")
  7. defer func() {
  8. if rows != nil {
  9. _ = rows.Close()
  10. }
  11. }()
  12. for rows.Next() {
  13. //var value *TimestampPb //可以工作
  14. //var value TimestampPb //可以工作
  15. //var value *timestamppb.Timestamp //不起作用
  16. //testStruct := test{} //不起作用
  17. //err := rows.Scan(&value) //扫描到变量
  18. //err := rows.Scan(&testStruct.date) //扫描到结构体中的字段
  19. if err != nil {
  20. log.Fatalln(err)
  21. }
  22. }
  23. }
  24. func (tpb *TimestampPb) Scan(src any) error {
  25. return nil
  26. }

我想知道是否可以使用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

  1. type Stuff struct { //this struct non-local, this is from third-party package
  2. Date *timestamppb.Timestamp
  3. }
  4. stuff = Stuff{}
  5. 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:

  1. type Test struct {
  2. }
  3. func (t Test) Scan(src any) error {
  4. //convert value
  5. }

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

  1. type TimestampPb timestamppb.Timestamp
  2. func (t TimestampPb) Scan(src any) error {
  3. //convert value
  4. }

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

  1. 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

  1. type TimestampPb timestamppb.Timestamp
  2. type test struct { //third-party struct, I can't modify it
  3. date *timestamppb.Timestamp
  4. }
  5. func (s *Storage) test() {
  6. rows, _ := s.db.Query("SELECT \"test_date\" FROM \"test_table\" LIMIT 1")
  7. defer func() {
  8. if rows != nil {
  9. _ = rows.Close()
  10. }
  11. }()
  12. for rows.Next() {
  13. //var value *TimestampPb //works
  14. //var value TimestampPb //works
  15. //var value *timestamppb.Timestamp //doesn't work
  16. //testStruct := test{} //doesn't work
  17. //err := rows.Scan(&value) //scan to variable
  18. //err := rows.Scan(&testStruct.date) //scan to field in the struct
  19. if err != nil {
  20. log.Fatalln(err)
  21. }
  22. }
  23. }
  24. func (tpb *TimestampPb) Scan(src any) error {
  25. return nil
  26. }

I wonder if I can use case with the testStruct?

答案1

得分: 0

pq库不支持*timestamppb.Timestamp作为时间戳类型。请参阅文档中支持的日期类型

需要使用不同的类型进行扫描。

通常我会在函数中使用辅助类型来实现。例如:

  1. type row struct {
  2. Date *timestamppb.Timestamp
  3. // ... 其他字段
  4. }
  5. func Query() ([]row, error) {
  6. rows, err := s.db.Query(...)
  7. if err != nil {
  8. return nil, err
  9. }
  10. defer rows.Close()
  11. var rows []row
  12. for rows.Next() {
  13. type aux struct {
  14. Date time.Time
  15. // ... 其他字段
  16. }
  17. var value aux
  18. if err := rows.Scan(&value); err != nil {
  19. return nil, err
  20. }
  21. rows = append(rows, row{
  22. Date: timestamppb.New(value.Date),
  23. // ... 其他字段
  24. }
  25. }
  26. return rows, nil
  27. }

希望这可以帮助到你!

英文:

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:

  1. type row struct {
  2. Date *timestamppb.Timestamp
  3. // ... more fields here
  4. }
  5. func Query() ([]row, error) {
  6. rows, err := s.db.Query(...)
  7. if err != nil {
  8. return nil, err
  9. }
  10. defer rows.Close()
  11. var rows []row
  12. for rows.Next() {
  13. type aux struct {
  14. Date time.Time
  15. // ... more fields here
  16. }
  17. var value aux
  18. if err := rows.Scan(&value); err != nil {
  19. return nil, err
  20. }
  21. rows = append(rows, row{
  22. Date: timestamppb.New(value.Date),
  23. // ... more fields here
  24. }
  25. }
  26. return rows, nil
  27. }

huangapple
  • 本文由 发表于 2023年3月17日 05:28:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/75761792.html
匿名

发表评论

匿名网友

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

确定