如何在1.18中使用golang泛型来简化代码片段?
How can I use golang generics in 1.18 to simplify the code snippet?
我一直在使用由 OpenAPI generator 在单独的包中生成的 2 个 SDK,它们共享在不同包中重复的相同代码 foo
、bar
:
package foo
// there's the exact same piece of code under another package bar
// GenericOpenAPIError Provides access to the body, error and model on returned errors.
type GenericOpenAPIError struct {
...
model interface{}
}
// Model returns the unpacked model of the error
func (e GenericOpenAPIError) Model() interface{} {
return e.model
}
// Failure Provides information about problems encountered while performing an operation.
type Failure struct {
// List of errors which caused this operation to fail
Errors []Error `json:"errors"`
}
// GetErrors returns the Errors field value
func (o *Failure) GetErrors() []Error {...}
// Error Describes a particular error encountered while performing an operation.
type Error struct {
...
// A human-readable explanation specific to this occurrence of the problem.
Detail *string `json:"detail,omitempty"`
...
}
另外,有一个应用程序我同时使用了两个 SDK:foo
、bar
并从错误列表中提取第一个错误,我尝试将错误转换为每个 SDK 的错误类型并且因此必须复制代码。有没有办法使用支持 generics 的 1.18
来简化它?
import (
foo "..."
bar "..."
)
func getErrorMessage(err error) (string) {
result := err.Error()
if fooError, ok1 := err.(foo.GenericOpenAPIError); ok1 {
if fooFailure, ok2 := fooError.Model().(foo.Failure); ok1 {
fooFailureErrors := fooFailure.GetErrors()
// it's guaranteed to be non-empty and .Detail != nil
result = *fooFailureErrors[0].Detail
}
}
if barError, ok1 := err.(bar.GenericOpenAPIError); ok2 {
if barFailure, ok2 := barError.Model().(bar.Failure); ok2 {
barFailureErrors := barFailure.GetErrors()
// it's guaranteed to be non-empty and .Detail != nil
result = *barFailureErrors[0].Detail
}
}
return result
}
首先,我想我可以在我的应用程序代码中将这些常见类型重新声明为接口,例如:
type GenericOpenAPIError interface {
Model() interface{}
}
...
type Failure interface {
GetErrors() []Error
}
type DetailedError interface {
GetDetail() string
}
然后直接转换到这些接口,这样的做法合理吗?
if mainError, ok1 := err.(GenericOpenAPIError); ok1 {
var mainErrorModel = mainError.Model()
if failure2, ok2 := mainErrorModel.(Failure); ok2 {
var ff = failure2.GetErrors()
if len(ff) > 0 {
result := ff[0].GetDetail()
}
}
}
当我尝试测试它时,好像
if failure2, ok2 := mainErrorModel.(Failure); ok2 {
这次选角不成功。我该如何调试它?这个问题似乎与 GetErrors
is a method on pointer 的面有关:
func (o *Failure) GetErrors() []Error {...}
而且我在这里不使用指针:
if failure2, ok2 := mainErrorModel.(Failure); ok2 {
好像有关系
您可以使用类型断言将 mainErrorModel
更改为 struct with no pointer
,然后将该结构更改为 struct with pointer
,最后您可以再次更改为类型 Failure
接口类型断言。
这就是例子。
// You can edit this code!
// Click here and start typing.
package main
import (
"fmt"
)
type GenericOpenAPIError interface {
Model() any
}
type Failure interface {
GetErrors() string
}
type GenericOpenAPI struct {
model any
}
func (g *GenericOpenAPI) Model() any {
return g.model
}
func (g *GenericOpenAPI) Error() string {
return "goae error"
}
type A struct {
b string
}
func (a *A) GetErrors() string {
return a.b
}
type B struct {
c string
}
func (b *B) GetErrors() string {
return b.c
}
type GetErrores interface {
A | B
}
func getErrorMessage[T GetErrores](err error) string {
if mainError, ok1 := err.(GenericOpenAPIError); ok1 {
var mainErrorModel = mainError.Model()
if t, ok := mainErrorModel.(T); ok {
if f, ok := any(&t).(Failure); ok { // must change &t to any then type assert to Failure to tell app that there is a method GetErrors()
return f.GetErrors()
}
}
}
return err.Error()
}
func main() {
var err any
err = &GenericOpenAPI{A{b: "this is error a"}}
e, ok := err.(error)
if !ok {
panic("not ok!")
}
fmt.Println(getErrorMessage[A](e))
// change model to B
err = &GenericOpenAPI{B{c: "this is error b"}}
e, ok = err.(error)
if !ok {
panic("not ok!")
}
fmt.Println(getErrorMessage[B](e))
}
我一直在使用由 OpenAPI generator 在单独的包中生成的 2 个 SDK,它们共享在不同包中重复的相同代码 foo
、bar
:
package foo
// there's the exact same piece of code under another package bar
// GenericOpenAPIError Provides access to the body, error and model on returned errors.
type GenericOpenAPIError struct {
...
model interface{}
}
// Model returns the unpacked model of the error
func (e GenericOpenAPIError) Model() interface{} {
return e.model
}
// Failure Provides information about problems encountered while performing an operation.
type Failure struct {
// List of errors which caused this operation to fail
Errors []Error `json:"errors"`
}
// GetErrors returns the Errors field value
func (o *Failure) GetErrors() []Error {...}
// Error Describes a particular error encountered while performing an operation.
type Error struct {
...
// A human-readable explanation specific to this occurrence of the problem.
Detail *string `json:"detail,omitempty"`
...
}
另外,有一个应用程序我同时使用了两个 SDK:foo
、bar
并从错误列表中提取第一个错误,我尝试将错误转换为每个 SDK 的错误类型并且因此必须复制代码。有没有办法使用支持 generics 的 1.18
来简化它?
import (
foo "..."
bar "..."
)
func getErrorMessage(err error) (string) {
result := err.Error()
if fooError, ok1 := err.(foo.GenericOpenAPIError); ok1 {
if fooFailure, ok2 := fooError.Model().(foo.Failure); ok1 {
fooFailureErrors := fooFailure.GetErrors()
// it's guaranteed to be non-empty and .Detail != nil
result = *fooFailureErrors[0].Detail
}
}
if barError, ok1 := err.(bar.GenericOpenAPIError); ok2 {
if barFailure, ok2 := barError.Model().(bar.Failure); ok2 {
barFailureErrors := barFailure.GetErrors()
// it's guaranteed to be non-empty and .Detail != nil
result = *barFailureErrors[0].Detail
}
}
return result
}
首先,我想我可以在我的应用程序代码中将这些常见类型重新声明为接口,例如:
type GenericOpenAPIError interface {
Model() interface{}
}
...
type Failure interface {
GetErrors() []Error
}
type DetailedError interface {
GetDetail() string
}
然后直接转换到这些接口,这样的做法合理吗?
if mainError, ok1 := err.(GenericOpenAPIError); ok1 {
var mainErrorModel = mainError.Model()
if failure2, ok2 := mainErrorModel.(Failure); ok2 {
var ff = failure2.GetErrors()
if len(ff) > 0 {
result := ff[0].GetDetail()
}
}
}
当我尝试测试它时,好像
if failure2, ok2 := mainErrorModel.(Failure); ok2 {
这次选角不成功。我该如何调试它?这个问题似乎与 GetErrors
is a method on pointer 的面有关:
func (o *Failure) GetErrors() []Error {...}
而且我在这里不使用指针:
if failure2, ok2 := mainErrorModel.(Failure); ok2 {
您可以使用类型断言将 mainErrorModel
更改为 struct with no pointer
,然后将该结构更改为 struct with pointer
,最后您可以再次更改为类型 Failure
接口类型断言。
这就是例子。
// You can edit this code!
// Click here and start typing.
package main
import (
"fmt"
)
type GenericOpenAPIError interface {
Model() any
}
type Failure interface {
GetErrors() string
}
type GenericOpenAPI struct {
model any
}
func (g *GenericOpenAPI) Model() any {
return g.model
}
func (g *GenericOpenAPI) Error() string {
return "goae error"
}
type A struct {
b string
}
func (a *A) GetErrors() string {
return a.b
}
type B struct {
c string
}
func (b *B) GetErrors() string {
return b.c
}
type GetErrores interface {
A | B
}
func getErrorMessage[T GetErrores](err error) string {
if mainError, ok1 := err.(GenericOpenAPIError); ok1 {
var mainErrorModel = mainError.Model()
if t, ok := mainErrorModel.(T); ok {
if f, ok := any(&t).(Failure); ok { // must change &t to any then type assert to Failure to tell app that there is a method GetErrors()
return f.GetErrors()
}
}
}
return err.Error()
}
func main() {
var err any
err = &GenericOpenAPI{A{b: "this is error a"}}
e, ok := err.(error)
if !ok {
panic("not ok!")
}
fmt.Println(getErrorMessage[A](e))
// change model to B
err = &GenericOpenAPI{B{c: "this is error b"}}
e, ok = err.(error)
if !ok {
panic("not ok!")
}
fmt.Println(getErrorMessage[B](e))
}