英文:
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
waterfallConfig
是 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.
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{})
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论