访问自定义类型的原始函数

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

Access Custom Type's Original Functions

问题

如何在Go中访问自定义类型的原始函数?

我不认为这与gqlgen有关,但我的用例与他们的框架密切相关。我正在尝试在gqlgen中为日期构建自定义标量,按照他们在这里的设置:https://gqlgen.com/reference/scalars/

我在MarshalGQL函数中遇到了困难。以下是我目前的代码:

package scalars

import (
	"fmt"
	"io"
	"time"
)

type Date time.Time

// UnmarshalGQL implements the graphql.Unmarshaler interface
func (date *Date) UnmarshalGQL(value interface{}) error {
	dateAsString, ok := value.(string)
	if !ok {
		return fmt.Errorf("date must be a string")
	}

	parsedTime, err := time.Parse(time.RFC3339, dateAsString)
	if err != nil {
		return fmt.Errorf("failed to convert given date value (%s) to Time struct", dateAsString)
	}
	*date = Date(parsedTime)
	return nil
}

// MarshalGQL implements the graphql.Marshaler interface
func (date Date) MarshalGQL(w io.Writer) {
    // 这一行导致编译错误
	w.Write(date.Format(time.RFC3339))
}

我的问题在于这一行:w.Write(date.Format(time.RFC3339))

它给我返回了这个编译错误:

date.Format未定义(类型Date没有字段或方法Format)编译器(MissingFieldOrMethod)

我理解它为什么这样说。根据编译器的了解,Date类型只有两个函数,即UnmarshalGQLMarshalGQL,它们都在这个文件中声明。然而,我想要访问time.Time类型的函数,以便格式化返回的日期。我该如何做到这一点?

英文:

How do I access the original functions of a customized type in Go?

I do not believe this is not specific to gqlgen, but my use-case heavily involves their framework. I'm trying to build a custom scalar for dates in gqlgen following their setup here: https://gqlgen.com/reference/scalars/

I'm having difficulty with the MarshalGQL function. Here is the code I have so far:

package scalars

import (
	"fmt"
	"io"
	"time"
)

type Date time.Time

// UnmarshalGQL implements the graphql.Unmarshaler interface
func (date *Date) UnmarshalGQL(value interface{}) error {
	dateAsString, ok := value.(string)
	if !ok {
		return fmt.Errorf("date must be a string")
	}

	parsedTime, err := time.Parse(time.RFC3339, dateAsString)
	if err != nil {
		return fmt.Errorf("failed to convert given date value (%s) to Time struct", dateAsString)
	}
	*date = Date(parsedTime)
	return nil
}

// MarshalGQL implements the graphql.Marshaler interface
func (date Date) MarshalGQL(w io.Writer) {
    // This line causes a compiler error
	w.Write(date.Format(time.RFC3339))
}

My issue is this line: w.Write(date.Format(time.RFC3339))

It's giving me this compiler error:

> date.Format undefined (type Date has no field or method Format) compiler (MissingFieldOrMethod)

I understand why it's saying this. The Date type only has two functions as far as the compiler knows, UnmarshalGQL and MarshalGQL, both of which are declared in this file. I want to get to the time.Time type's functions though so that I can format the date being returned. How do I go about doing this?

答案1

得分: 2

你可以按照mkopriva的建议,将Date显式转换为time.Time,然后进行格式化。

或者你可以通过嵌入time.Time来创建Date类型,这将使你可以访问time.Time的格式化方法。

package main

import (
	"fmt"
	"io"
	"time"
)

type Date struct {
	time.Time
}

func (date *Date) UnmarshalGQL(value interface{}) error {
	dateAsString, ok := value.(string)
	if !ok {
		return fmt.Errorf("date must be a string")
	}

	parsedTime, err := time.Parse(time.RFC3339, dateAsString)
	if err != nil {
		return fmt.Errorf("failed to convert given date value (%s) to Time struct", dateAsString)
	}
	*date = Date{parsedTime}
	return nil
}

// MarshalGQL implements the graphql.Marshaler interface
func (date Date) MarshalGQL(w io.Writer) {
	// This line causes a compiler error
	w.Write([]byte(date.Format(time.RFC3339)))
}

func NewDate(v time.Time) *Date {
	return &Date{v}
}

func main() {
}

Playground

为了更好地理解,你可以阅读golang-nuts中讨论的帖子。

> 按设计,“无法在非本地类型上定义新方法”。
>
> 最佳实践是将非本地类型嵌入到自己的本地类型中,并进行扩展。类型别名(type MyFoo Foo)创建了一个与原始类型(more-or-less)完全不同的类型。我不知道有没有一种简单/最佳实践的方法可以使用类型断言来解决这个问题。
>
> Peter Bourgon

还有:

> > 类型的方法在定义它的包中。
>
> 这是一种逻辑上的一致性。这是一种编译优势。它被视为大规模维护和多人开发项目的重要优势。
>
> 你所说的能力并没有丧失,因为你可以像上面描述的那样将基本类型嵌入到新类型中,并在其中添加任何你想要的内容,以你所寻求的功能性的“is a”方式,唯一的限制是你的新类型必须有一个新的名称,并且它的所有字段和方法必须在它的新包中。
>
> Michael Jones

英文:

You can either do as mkopriva suggested to explicitly convert Date to time.Time and then format

or you can create Date type by embedding time.time which will give access to time.Time format method

package main

import (
	"fmt"
	"io"
	"time"
)

type Date struct {
	time.Time
}

func (date *Date) UnmarshalGQL(value interface{}) error {
	dateAsString, ok := value.(string)
	if !ok {
		return fmt.Errorf("date must be a string")
	}

	parsedTime, err := time.Parse(time.RFC3339, dateAsString)
	if err != nil {
		return fmt.Errorf("failed to convert given date value (%s) to Time struct", dateAsString)
	}
	*date = Date{parsedTime}
	return nil
}

// MarshalGQL implements the graphql.Marshaler interface
func (date Date) MarshalGQL(w io.Writer) {
	// This line causes a compiler error
	w.Write([]byte(date.Format(time.RFC3339)))
}

func NewDate(v time.Time) *Date {
	return &Date{v}
}

func main() {
}

Playground

For more understanding you can read golang-nuts post which discuss this

> You "cannot define new methods on non-local type

展开收缩
," by design.
>
> The best practice is to embed the non-local type into your own own local type, and extend it. Type-aliasing (type MyFoo Foo) creates a type that is (more-or-less) completely distinct from the original. I'm not aware of a straightforward/best-practice way to use type assertions to get around that.
>
> Peter Bourgon

And:

> > A type's methods are in the package that defines it.
>
> This is a logical coherence. It is a compilation virtue. It is seen as an important benefit for large-scale maintenance and multi-person development projects.
>
> The power you speak of is not lost, though, because you can embed the base type in a new type as described above and add whatever you want to it, in the kind of functional "is a" that you seek, with the only caveat that your new type must have a new name, and all of its fields and methods must be in its new package.
>
> Michael Jones

huangapple
  • 本文由 发表于 2022年9月6日 12:11:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/73616632.html
匿名

发表评论

匿名网友

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

确定