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

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

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
}

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:

确定