英文:
How to write append "method" in go
问题
我想探索一下Go的可能性。你如何将"append"转换为方法?简单地说:"st.app(t) == append(st, t)"
这是我得到的代码:
type T interface{}
type ST []interface{}
func (st []T) app(t T) []T {
return append(st, t)
}
但是这段代码没有检查类型的兼容性:
append([]int{1,2},"a") // 正确地报错
ST{1,2}.app("a") // 愚蠢地返回 [1 2 a] !!!
我知道为什么这段代码没有检查类型的兼容性,但正确的做法是什么?有可能吗?
我很感谢你帮助我理解Go的工作原理。
英文:
I would like to explore Go possibilities. How would you turn "append" to method? simply: "st.app(t) == append(st, t)"
This is what I got:
type T interface{}
type ST []interface{}
func (st []T) app (t T) []T {
return(append(st, t))
}
but this code does not check for type compatibility:
append([]int{1,2},"a") // correctly gives error
ST{1,2}.app("a") // dumbly gives [1 2 a] !!!
I know why the code does not check for type compatibility but What is the right way to do it? Is it possible?
I appreciate your help to understand how Go works.
答案1
得分: 2
正确的做法是将append()
作为内置函数调用。它之所以是内置函数并非偶然,因为在Go中无法直接编写append
函数本身,而且任何类似的实现都可能不安全(且过于复杂)。
不要与Go作对。只需编写这一行代码。在尝试创建复杂的泛型时,你可以解决三个以上的问题并发布产品。这就是Go的工作方式。
这并不意味着Go永远不会支持泛型,但在我们迈向Go 2的过程中,请考虑来自Russ Cox的以下观点:
>例如,我最近一直在研究泛型,但我脑海中并没有一个清晰的图景,来描述Go用户需要泛型解决的具体问题。因此,我无法回答一个设计问题,比如是否支持泛型方法,也就是说方法的参数与接收者是分开参数化的。如果我们有一系列大量的实际用例,我们可以通过研究这些用例来开始回答这样的问题。
(另一方面,泛型是ExperienceReports中回应最多的主题,所以并不是没有人意识到这个需求。但不要与Go作对。它是一门用于发布优秀软件的绝妙语言。然而,它并不是一门泛型语言。)
英文:
The right way to do this is to call append()
as the built-in function it is. It's not an accident that it's a built-in function; you can't write append
itself in Go, and anything that approximates it would be unsafe (and over-complicated).
Don't fight Go. Just write the line of code. In the time you spend trying to create tricky generics, you could have solved three more issues and shipped the product. That's how Go works.
This isn't to say Go will never have generics, but as we move towards Go 2 consider the following from Russ Cox:
>For example, I've been examining generics recently, but I don't have in my mind a clear picture of the detailed, concrete problems that Go users need generics to solve. As a result, I can't answer a design question like whether to support generic methods, which is to say methods that are parameterized separately from the receiver. If we had a large set of real-world use cases, we could begin to answer a question like this by examining the significant ones.
(On the other hand, Generics is the most responded-to topic in ExperienceReports, so it's not that no one is aware of the interest. But don't fight Go. It's a fantastic language for shipping great software. It is not, however, a generic language.)
答案2
得分: 1
4年后,实际上有一种可能的解决方案,可以将方法与接收器分开参数化。
- Go 1.18泛型中有这个功能。
- 它不支持参数化方法。
- 它支持参数化方法,这意味着接收器可以具有类型参数。
- Jaana B. Dogan基于此提出了“Go中的泛型辅助工具”。
参数化接收器是一个有用的工具,帮助我开发了一种常见模式,即辅助工具,以克服没有参数化方法的缺点。
辅助工具只是一种新类型,它可以访问您希望在通用方法上具有的类型。
她的示例:
package database
type Client struct{ ... }
type Querier[T any] struct {
client *Client
}
func NewQuerier[T any](c *Client) *Querier[T] {
return &Querier[T]{
client: c,
}
}
func (q *Querier[T]) All(ctx context.Context) ([]T, error) {
// 实现
}
func (q *Querier[T]) Filter(ctx context.Context, filter ...Filter) ([]T, error) {
// 实现
您可以这样使用它:
querier := database.NewQuerier[Person](client)
然而,在您的情况下,虽然您可以定义一个Appender
结构体,但它仍然需要应用于具体的结构体,这就是为什么使用内置的append()
函数仍然更可取的原因。
但是总的来说,如果您需要参数化方法,Jaana的模式可以帮助您。
英文:
4 years later, there is actually a possible solution to get methods that are parameterized separately from the receiver.
- There is with Go 1.18 generics
- It does not support parameterized method
- It does support parameterized method, meaning the receiver may have type parameters
- Jaana B. Dogan proposes "Generics facilitators in Go" based on that
> Parameterized receivers are a useful tool and helped me develop a common pattern, facilitators, to overcome the shortcomings of having no parameterized methods.
Facilitators are simply a new type that has access to the type you wished you had generic methods on.
package database
type Client struct{ ... }
type Querier[T any] struct {
client *Client
}
func NewQuerier[T any](c *Client) *Querier[T] {
return &Querier[T]{
client: c,
}
}
func (q *Querier[T]) All(ctx context.Context) ([]T, error) {
// implementation
}
func (q *Querier[T]) Filter(ctx context.Context, filter ...Filter) ([]T, error) {
// implementation
You can use it as:
querier := database.NewQuerier[Person](client)
In your case though, while you could define an Appender
struct, it still needs to apply on a concrete struct, which is why using the built-in append()
remains preferable.
But in general, should you need parameterized methods, Jaana's pattern can help.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论