How to access shared field in structs in generics in Go 1.18? I get error "type t has no field or method DATE_START"

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

How to access shared field in structs in generics in Go 1.18? I get error "type t has no field or method DATE_START"

问题

我有两个结构体,它们具有相同的字段名称和类型:

type JOURNAL_TAG struct {
	DATE_START            time.Time
	DATE_END              time.Time
	ENTRY_NUMBER          uint
	VALUE                 float64
}

type INVENTORY_TAG struct {
	DATE_START   time.Time
	DATE_END     time.Time
	PRICE        float64
	QUANTITY     float64
	ACCOUNT_NAME string
}

我有一个函数,它访问了公共字段DATE_START,应该对这些类型的切片进行排序:

func sort_by_time[t JOURNAL_TAG|INVENTORY_TAG](slice []t, is_ascending bool) {
	sort.Slice(slice, func(i, j int) bool {
		return slice[i].DATE_START.After(slice[j].DATE_START) == is_ascending
	})
}

运行go build报告了一个编译器错误:

slice[i].DATE_START undefined (type t has no field or method DATE_START)

我想使用泛型对这两种类型的切片进行排序,这是可能的吗?

我正在使用go 1.18版本。

英文:

I have two structs that have the some of the same field names and types:

type JOURNAL_TAG struct {
	DATE_START            time.Time
	DATE_END              time.Time
	ENTRY_NUMBER          uint
	VALUE                 float64
}

type INVENTORY_TAG struct {
	DATE_START   time.Time
	DATE_END     time.Time
	PRICE        float64
	QUANTITY     float64
	ACCOUNT_NAME string
}

and I have a func that accesses the common field DATE_START that should sort the slices of these types:

func sort_by_time[t JOURNAL_TAG|INVENTORY_TAG](slice []t, is_ascending bool) {
	sort.Slice(slice, func(i, j int) bool {
		return slice[i].DATE_START.After(slice[j].DATE_START) == is_ascending
	})
}

Running go build reports a compiler error:

slice[i].DATE_START undefined (type t has no field or method DATE_START)

I want to sort the slices of these two types using generics, is it possible?

I am using go 1.18.

答案1

得分: 13

Go 1.18发布说明中:

即使类型参数的类型集中的所有类型都有字段f,Go编译器也不支持访问类型参数类型x的字段x.f。我们可能会在Go 1.19中删除此限制。

例如,您可以为每个结构体添加一个DateStart() time.Time方法,该方法返回DATE_START字段,并且如果您想使用泛型,可以将该方法用作type constraint的一部分。

也就是说,您不需要泛型来解决这个特定的问题。即使没有泛型,您也可以定义一个接口:

type SomeInterface interface {
	DateStart() time.Time
}

然后进行排序:

items := []SomeInterface{
	INVENTORY_TAG{...},
	INVENTORY_TAG{...},
}
sort.Slice(items, func(i, j int) bool { return items[i].DateStart().Before(items[j].DateStart()) })
英文:

From the Go 1.18 release notes:

> The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter's type set have a field f. We may remove this restriction in Go 1.19.

You could for example add a DateStart() time.Time method to each of the structs that returns the DATE_START field, and then use that method as part of your type constraint if you wanted to use generics.

That said, you don't need generics for this specific problem. Even without generics, you could define an interface:

type SomeInterface interface {
	DateStart() time.Time
}

and then sort:

items := []SomeInterface{
	INVENTORY_TAG{...},
	INVENTORY_TAG{...},
}
sort.Slice(items, func(i, j int) bool { return items[i].DateStart().Before(items[j].DateStart()) })

答案2

得分: 5

根据@thepudds的说法,在这种情况下最好使用接口实现,但如果你想尝试泛型,你可以这样做:

package main

import (
	"fmt"
	"sort"
	"time"
)

type JOURNAL_TAG struct {
	DATE_START   time.Time
	DATE_END     time.Time
	ENTRY_NUMBER uint
	VALUE        float64
}

type INVENTORY_TAG struct {
	DATE_START   time.Time
	DATE_END     time.Time
	PRICE        float64
	QUANTITY     float64
	ACCOUNT_NAME string
}

type hasDateInterface interface {
	DateStart() time.Time
}

func (j JOURNAL_TAG) DateStart() time.Time {
	return j.DATE_START
}

func (i INVENTORY_TAG) DateStart() time.Time {
	return i.DATE_START
}

func sort_by_time[t hasDateInterface](slice []t, is_ascending bool) {
	sort.Slice(slice, func(i, j int) bool {
		return slice[i].DateStart().After(slice[j].DateStart()) == is_ascending
	})
}

func main() {
	journalTags := []JOURNAL_TAG{
		{
			DATE_START:   time.Date(2023, time.August, 1, 0, 0, 0, 0, time.UTC),
			DATE_END:     time.Date(2023, time.August, 2, 0, 0, 0, 0, time.UTC),
			ENTRY_NUMBER: 1,
			VALUE:        10.5,
		},
		{
			DATE_START:   time.Date(2023, time.August, 3, 0, 0, 0, 0, time.UTC),
			DATE_END:     time.Date(2023, time.August, 4, 0, 0, 0, 0, time.UTC),
			ENTRY_NUMBER: 2,
			VALUE:        20.5,
		},
	}

	inventoryTags := []INVENTORY_TAG{
		{
			DATE_START:   time.Date(2023, time.August, 5, 0, 0, 0, 0, time.UTC),
			DATE_END:     time.Date(2023, time.August, 6, 0, 0, 0, 0, time.UTC),
			PRICE:        100.0,
			QUANTITY:     5.0,
			ACCOUNT_NAME: "Inventory 1",
		},
		{
			DATE_START:   time.Date(2023, time.August, 7, 0, 0, 0, 0, time.UTC),
			DATE_END:     time.Date(2023, time.August, 8, 0, 0, 0, 0, time.UTC),
			PRICE:        200.0,
			QUANTITY:     10.0,
			ACCOUNT_NAME: "Inventory 2",
		},
	}

	sort_by_time(journalTags, true)
	sort_by_time(inventoryTags, false)

	fmt.Println("Sorted Journal Tags:")
	for _, tag := range journalTags {
		fmt.Println(tag)
	}

	fmt.Println("\nSorted Inventory Tags:")
	for _, tag := range inventoryTags {
		fmt.Println(tag)
	}
}

你可以在这里尝试运行代码:https://go.dev/play/p/Xak4uzCNhE-

英文:

Like @thepudds says in this case is better use a interface implementation, but if you want try generics you could do:

package main

type JOURNAL_TAG struct {
    DATE_START            time.Time
    DATE_END              time.Time
    ENTRY_NUMBER          uint
    VALUE                 float64
}

type INVENTORY_TAG struct {
    DATE_START   time.Time
    DATE_END     time.Time
    PRICE        float64
    QUANTITY     float64
    ACCOUNT_NAME string
}

type hasDateInterface interface {
    DateStart() time.Time
}

func (j JOURNAL_TAG) DateStart(){
   return j.DATE_START
}

func (i INVENTORY_TAG) DateStart(){
   return i.DATE_START
}

func sort_by_time[t hasDateInterface](slice []t, is_ascending bool) {
    sort.Slice(slice, func(i, j int) bool {
        return slice[i].DATE_START.After(slice[j].DateStart()) == is_ascending
    })
}

tried
https://go.dev/play/p/Xak4uzCNhE-

huangapple
  • 本文由 发表于 2022年3月19日 01:15:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/71530811.html
匿名

发表评论

匿名网友

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

确定