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

王林
Release: 2024-02-06 08:18:11
forward
1085 people have browsed it

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)
Copy after login

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
}
Copy after login

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
}
Copy after login

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 {
Copy after login

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
}
Copy after login

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
}
Copy after login

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!

source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template