Home  >  Article  >  Backend Development  >  How to add interface to non-native structure in golang?

How to add interface to non-native structure in golang?

王林
王林forward
2024-02-06 08:18:111089browse

How to add interface to non-native structure in golang?

Question content

I use https://github.com/lib/pq to get data from postgres. To extract the data I use a third party structure which contains the protobuf timestamp field https://pkg.go.dev/google.golang.org/protobuf/types/known/timestamppb#timestamp so So the problem is to get the job to 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 try to scan the structure implementing the sql.scanner interface. That's simple. I just implemented the 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 a non-native type. Then I tried defining the local type and doing the same

type timestamppb timestamppb.timestamp

func (t timestamppb) scan(src any) error {
//convert value
}

But this trick didn’t work. Additionally, I have the warning "'scan' passes lock by value: type 'timestamppb' contains 'protoimpl.messagestate' contains 'sync.mutex', i.e. 'sync.locker'" This doesn't work if I specify a pointer for timestamppb

func (t *timestamppb) scan(src any) error {

So I want to know, how to make a timestamppb.timestamp instance of sql.scanner. is it possible?

Update 1 Small example of what 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 want to know if I can use case with teststruct?


Correct answer


pq The library does not support *timestamppb.timestamp as a timestamp type. See the documentation for supported date types.

Different types are required when scanning.

I usually do this by using a helper 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
}

The above is the detailed content of How to add interface to non-native structure in golang?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete