用类型声明替换Go中的包装结构体

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

Replace wrapping struct with a type declaration in Go

问题

我想扩展Go标准库中的regexp,以便能够定义自己的方法。我使用以下结构体:

type RichRegexp struct {
    *regexp.Regexp
}

如你所见,这个结构体除了包装的regexp.Regexp之外没有其他内容。所以我想知道是否可以用一个简单的类型声明来替代它,像这样:

type RichRegexp regexp.Regexp

但是接下来我应该如何编写以下函数呢?

func Compile(expression string) (*RichRegexp, error) {
    regex, err := regexp.Compile(expression)
    if err != nil {
        return nil, err
    }
    return &RichRegexp{regex}, nil // 如何实现这个?
}

我尝试将regexp.Regexp转换为我的RichRegexp,但它无法编译通过。返回一个包装了底层类型的自定义类型的一般模式是什么?

英文:

I want to extend the regexp from the Go standard library to be able to define my own methods. I use the following struct:

type RichRegexp struct {
    *regexp.Regexp
}

As you can see, this struct contains nothing but the wrapped regexp.Regexp. So I wonder whether I could replace this with a simple type declaration like this:

type RichRegexp regexp.Regexp

But how should I write the following func then?

func Compile(expression string) (*RichRegexp, error) {
	regex, err := regexp.Compile(expression)
	if err != nil {
		return nil, err
	}
	return &RichRegexp{regex}, nil // How to do this?
}

I tried to convert regexp.Regexp to my RichRegexp but it didn't compile. What is the general pattern to return a custom type which wraps a underlying type?

答案1

得分: 5

你可以使用类型转换,但在这种情况下,你的类型定义不能是指针:

type MyRegexp *regexp.Regexp // 不起作用

这是由规范支持的:

接收器类型必须是T或*T的形式,其中T是类型名称。
T所表示的类型称为接收器基本类型;它不能是指针或接口类型,并且必须在同一个包中声明。
该方法被绑定到基本类型,并且该方法名仅在该类型的选择器中可见。

然而,你可以这样做:

type MyRegexp regexp.Regexp

由于你现在处理的是值,你可以这样做:

x := regexp.MustCompile(".*")
y := MyRegexp(*x)

然后你就有了自己的正则表达式类型。

完整代码请参见:http://play.golang.org/p/OWNdA2FinN

作为一般模式,我会说:

  • 如果不太可能改变且不需要存储任意值,请使用类型转换。
  • 如果需要将值与嵌入类型一起存储,请使用struct
  • 如果你的代码可能会改变并且需要支持各种各样的事物,请定义一个接口,不要使用嵌入/类型转换。
英文:

You can use a conversion, but in this case it is necessary, that your type definition is not a pointer:

type MyRegexp *regexp.Regexp // Doesn't work

This is backed by the spec:

> The receiver type must be of the form T or *T where T is a type name.
> The type denoted by T is called the receiver base type; it must not be
> a pointer or interface type
and it must be declared in the same
> package as the method. The method is said to be bound to the base type
> and the method name is visible only within selectors for that type.

However, you can do this:

type MyRegexp regexp.Regexp

As you're handling values now, you can do the following:

x := regexp.MustCompile(".*")
y := MyRegexp(*x)

And you have your own regexp type.

Full code at play: http://play.golang.org/p/OWNdA2FinN

As a general pattern, I would would say:

  • If it's unlikely to change and you don't need to store arbitrary values, use
    a type conversion.
  • If you need to store values along with your embedded type, use a struct.
  • If your code is likely to change and needs to support large varieties of things,
    define an interface and don't use embedding / type conversion.

答案2

得分: 3

package main

import (
"regexp"
)

type RichRegexp regexp.Regexp

func Compile(expression string) (*RichRegexp, error) {
regex, err := regexp.Compile(expression)
if err != nil {
return nil, err
}

return (*RichRegexp)(regex), nil

}

func main() {
Compile("foo")
}

英文:
package main

import (
        "regexp"
)

type RichRegexp regexp.Regexp

func Compile(expression string) (*RichRegexp, error) {
        regex, err := regexp.Compile(expression)
        if err != nil {
                return nil, err
        }

        return (*RichRegexp)(regex), nil
}

func main() {
    Compile("foo")
}

Also here: http://play.golang.org/p/cgpi8z2CfF

huangapple
  • 本文由 发表于 2012年11月4日 21:53:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/13219132.html
匿名

发表评论

匿名网友

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

确定