Go结构体(类型为any)的自定义类型强制执行

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

Custom type enforcing for Go structs (type any)

问题

我对Go中的自定义类型有一个问题。我想要使用any来实现某种类型安全性。我知道TypeScript中的联合类型,但据我所知,Go中不支持联合类型。有什么可行的替代方案吗?

我希望函数或字段的类型为any,因为我事先不知道类型。但我也希望限制可能的类型,以便编译器对于意外的类型发出警告。

据我所知,这对于常规的自定义类型(如type x string等)是有效的,但对于struct类型无效。

以下是一个带有配置的示例:

package main

import "fmt"

type plantConfig any

type treeConfig struct {
	trunkDiameter float64
}

func newTreeConfig(config treeConfig) plantConfig {
	return config
}

type flowerConfig struct {
	color string
}

func newFlowerConfig(config flowerConfig) plantConfig {
	return config
}

// newGarden应该只接受plantConfig类型。
func newGarden(config plantConfig) {
	switch config.(type) {
	case treeConfig:
		fmt.Println("planted trees")
	case flowerConfig:
		fmt.Println("planted flowers")
	default:
		fmt.Println("unknown plant")
	}
}

// waterfallConfig不是plantConfig类型。newGarden不应该接受它作为参数。
type waterfallConfig struct {
	height float64
}

func main() {
	// 这在编译时不会报告任何错误。
	newGarden(waterfallConfig{})
}

输出:

unknown plant

然而,我希望编译器对newGarden(waterfallConfig{})发出警告,因为waterfallConfig不是plantConfig类型,不应该进行隐式转换。
当然,我可以将plantConfig定义为一个带有相对“唯一”方法名的接口,但这对我来说不够清晰。有什么建议吗?或者我是否遗漏了一些明显的东西?

英文:

I have a question regarding custom types in Go. I'm trying to get some sort of type safety with any. I know union types from TypeScript but as far as I know, union types are not possible in Go. What are viable alternatives?

I want a function or field to have type any because I don't know the type upfront. But I also want to limit possible types so that the compiler complains for unexpected ones.

From what I know, this does work with regular custom types like type x string, etc. but not with struct.

Here is an example with configs:

package main

import "fmt"

type plantConfig any

type treeConfig struct {
	trunkDiameter float64
}

func newTreeConfig(config treeConfig) plantConfig {
	return config
}

type flowerConfig struct {
	color string
}

func newFlowerConfig(config flowerConfig) plantConfig {
	return config
}

// newGarden should only take plantConfig.
func newGarden(config plantConfig) {
	switch config.(type) {
	case treeConfig:
		fmt.Println("planted trees")
	case flowerConfig:
		fmt.Println("planted flowers")
	default:
		fmt.Println("unknown plant")
	}
}

// waterfallConfig is NOT a plantConfig. newGarden should not accept this as
// parameter.
type waterfallConfig struct {
	height float64
}

func main() {
	// This does not report any errors while compiling.
	newGarden(waterfallConfig{})
}

Output:

unknown plant

However, I'd expect the compiler to complain about newGarden(waterfallConfig{}) because waterfallConfig is not of type plantConfig and it should not cast implicitly.
Of course, I could make plantConfig an interface with a method name that is rather "unique" but this does not seem clean to me. Any suggestions? Or am I missing something obvious?

答案1

得分: 1

你正在走在正确的道路上,但是不是使用任何(interface{}),而是要使用一个限制你可以使用的类型的接口类型。所以像这样声明 plantConfig:

type plantConfig interface{ ImplementsPlant() }

然后你只需要说允许的类型实现该接口,像这样:

func (treeConfig) ImplementsPlant() {}

func (flowerConfig) ImplementsPlant() {}
英文:

You are on the right track but instead of any (interface{}) you want an interface type that restricts the types you can use. So declare plantConfig like this:

type plantConfig interface{ ImplementsPlant() }

Then you just need to say that the allowed types implement that interface like this:

func (treeConfig) ImplementsPlant() {}

func (flowerConfig) ImplementsPlant() {}

答案2

得分: 1

waterfallConfigany 类型。从名称上就可以看出来 Go结构体(类型为any)的自定义类型强制执行

基于 any 创建一个新类型只是创建了一个表示空接口的新符号,所有类型都将匹配它。

我能想到的唯一实现你想要的在编译时的方法是识别所有 PlantConfig 类型都将实现的某个函数,并且让 newGarden 只接受 PlantConfig 接口。

但是由于 Go 中的接口是鸭子类型而不是形式类型,任何 PlantConfig 接口的方法都需要与 waterfallConfig 可能实现的任何方法不同。

你甚至可以使用这个方法简化代码的其他方面。

也许可以这样做:

type PlantConfig interface {
   func PlantType() string
}

type LandscapeFeatureConfig interface {
   func FeatureType() string
}

// treeConfig 实现了 PlantConfig
type treeConfig struct {}

func (treeConfig) PlantType() string { return "tree" }


// flowerConfig 实现了 PlantConfig
type flowerConfig struct {}

func (flowerConfig) PlantType() string { return "flower" }


// waterfallConfig 实现了 LandscapeFeatureConfig
type waterfallConfig struct {}

func (waterfallConfig) FeatureType() string { return "waterfall" }



func newGarden(cfg PlantConfig) {
   fmt.Printf("planted %s\n", cfg.PlantType())
}

我假设瀑布是你可能希望区分的其他配置类型,将其称为“景观特征”;相应的 LandscapeFeatureConfig 接口用于标识它。这可能适用于你的情况,但仅供参考。

使用这种方法,以下代码将无法编译:

func main() {
    // newGarden 只接受 PlantConfig
    newGarden(waterfallConfig{})
}
英文:

waterfallConfig is of type any. The clue is in the name. Go结构体(类型为any)的自定义类型强制执行

Creating a new type based on any just creates a new symbol representing the empty interface, which all types will match.

The only way I can see to achieve what you want at compile time is to identify some function that all PlantConfig types will implement and have newGarden accept only PlantConfig interfaces.

But as interfaces in Go are duck-typed, rather than formal types, the method(s) of any PlantConfig interface will need to be distinct from any methods that your waterfallConfig might also implement.

You could even use this to simplify other aspects of your code.

Perhaps something like:

type PlantConfig interface {
   func PlantType() string
}

type LandscapeFeatureConfig interface {
   func FeatureType() string
}

// treeConfig implements PlantConfig
type treeConfig struct {}

func (treeConfig) PlantType() string { return "tree" }


// flowerConfig implements PlantConfig
type flowerConfig struct {}

func (flowerConfig) PlantType() string { return "flower" }


// waterfallConfig implements LandscapeFeatureConfig
type waterfallConfig struct {}

func (waterfallConfig) FeatureType() string { return "waterfall" }



func newGarden(cfg PlantConfig) {
   fmt.Printf("planted %s\n", cfg.PlantType())
}

I have imagined that a waterfall is some other configuration type that you may wish to differentiate, calling it a 'Landscape Feature'; a corresponding LandscapeFeatureConfig interface identifies this. This may or may not be suitable in your case, but it is intended as an illustration only.

With that approach, this code will now not compile:

func main() {
    // newGarden will only accept PlantConfig
    newGarden(waterfallConfig{})
}

huangapple
  • 本文由 发表于 2023年6月30日 06:36:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76584992.html
匿名

发表评论

匿名网友

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

确定