How to Create a Type in Go that Can Take Multiple Types

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

How to Create a Type in Go that Can Take Multiple Types

问题

如何创建一个可以接受布尔值、字符串或整数的自定义类型,而不使用any接口?

目前,如果你想支持多种类型,比如作为HashMap的值,你可以这样做:

data := make(map[string]interface{})
data["bool"] = true
data["string"] = "string"
data["int"] = 3

然后你可以将它传递给函数,例如:

func doStuff(data map[string]interface{}) {
  ...
}

我尝试了以下方法:

type CustomType interface {
	~int | ~bool | ~string
}

data := make(map[string]CustomType)

但是Go会报错,说我不能在泛型函数的类型约束之外使用它。

英文:

How would you go about creating your own type that can accept a bool, string or int without using the any interface?

Currently, if you want to support multiple types, say as a value to a HashMap, you can do something like:

data = make(map[string]any)
data["bool"] = true
data["string"] = "string"
data["int"] = 3

And then you can pass this around to functions like:

func doStuff(data map[string]any) {
  ...
}

I have tried the following:

type CustomType interface {
	~int | ~bool | ~string
}

data = make(map[string]CustomType)

But Go will complain that I can't use this outside of a type constraints for generic functions, etc.

答案1

得分: 0

这听起来像是你在寻找一个"sum type"。Go语言没有这样的类型,至少不是以正确的方式,但你有几个不同的选项来实现类似的功能:

第一种方法:创建一个包含你想要的所有类型作为字段的结构体。

type Sum struct {
  I int
  B bool
  S string
}

make(map[string]Sum)等情况下,这将完美地工作。但是,每当你想要从这个类型中获取底层的int、bool或string时,你都必须明确地请求你想要的类型。你也没有任何内置的机制来跟踪给定值中的哪种类型。你可以添加一些逻辑来跟踪它,可能会像这样:

type Sum struct {
  i int
  isInt bool

  b bool
  isBool bool

  s string
  isString bool
}

func Int(i int) Sum {
  return Sum{i: i, isInt: true}
}

func (s Sum) IsInt() bool {
  return s.IsInt
}

func (s Sum) Int() int {
  if !s.isInt {
    panic("not an int")
  }
  return s.i
}

// 类似的Bool()和String()函数。

理论上,如果你真的非常需要这段代码的每一位性能,你可以使用一些技巧,使其像C语言中描述的联合类型一样运行,具体可以参考这个问题

第二种方法是使用接口来实现类似的功能。这篇文章展示了如何实现。使用接口的优点是可以使用Go语言内置的类型切换语法来查找并确定你拥有的值的类型。

第三种方法是继续使用any。这种方法更简单,已经内置在语言中,并且相当高效。使用any的唯一缺点是,与其他选项不同,它有可能构造出意外类型的值(例如,你可以将一个float传递给接受any的函数,但你不能从一个float创建一个Sum值)。

英文:

This sounds like you're looking for a "sum type". Go doesn't have these, at least not in the proper sense, but you have a few different options for doing something similar:

First: just make a struct that has all the types you want as fields.

type Sum struct {
  I int
  B bool
  S string
}

This will work perfectly well in something like make(map[string]Sum), but any time you want to get the underlying ints, bools, or strings out of this type, you'll have to explicitly ask for the one you want. You also don't have any built-in mechanism to track which type of thing you have in a given value. You could add logic to track that that might look something like:

type Sum struct {
  i int
  isInt bool

  b bool
  isBool bool

  s string
  isString bool
}

func Int(i int) Sum {
  return Sum{i: i, isInt: true}
}

func (s Sum) IsInt() bool {
  return s.IsInt
}

func (s Sum) Int() int {
  if !s.isInt {
    panic("not an int")
  }
  return s.i
}

// Similar Bool() and String() functions.

In theory, if you really really really need every last bit of performance out of this code, you can do some tricks to make this act like a union type in C as described in this question.

Second, you can do something similar, but using interfaces. This post shows how that might work. The advantage to this is the ability to use Go's built-in type-switch syntax to look through and find which kind of value you have.

Third: just keep using any. This is simpler, already built-in, and reasonably efficient. The only downside to using any is that unlike the other options, it's possible to construct values with unexpected types (e.g. you can pass a float into something that accepts any, but you can't create a Sum value from a float).

huangapple
  • 本文由 发表于 2023年2月27日 15:07:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75577577.html
匿名

发表评论

匿名网友

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

确定