英文:
Go - storing structs with the same embedded struct in a list
问题
我有多个具有相同嵌入结构的结构体。在其中一个结构体中,我想存储任何嵌入基本结构的结构体的列表。这里是一个小片段展示了这种情况。
package main
type Symbol interface{}
type Base struct {
a int32
}
type Foo struct {
Base
b int32
Symbols []Base
// 下面的代码可以工作
// Symbols []Symbol
}
type Bar struct {
Base
c int32
}
func main () {
var bar Bar
var foo Foo
foo.Symbols = append(foo.Symbols, bar)
}
然而,这样做是不起作用的。我得到了./main.go:25:22: cannot use bar (type Bar) as type Base in append
的错误。当我使用空的Symbol
接口时,一切都正常工作。然而,这种方法完全绕过了类型系统,因为现在可以将任何东西存储在列表中。我希望以某种方式表示只有Base
、Foo
和Bar
可以存储在列表中,这样编译器可以检查是否满足此要求。我的结构体没有任何方法,肯定没有共享行为并且可以添加到接口中的方法。向接口添加一些虚拟方法和虚拟实现似乎非常不自然。在处理这种情况的Go语言惯用方式是什么?
英文:
I have multiple structs with the same embedded struct. In one of the structs I would like to store a list of any struct that embeds the base struct. Here is a small snippet showing the case.
package main
type Symbol interface{}
type Base struct {
a int32
}
type Foo struct {
Base
b int32
Symbols []Base
// Below works
// Symbols []Symbol
}
type Bar struct {
Base
c int32
}
func main () {
var bar Bar
var foo Foo
foo.Symbols = append(foo.Symbols, bar)
}
However, this does not work. I get ./main.go:25:22: cannot use bar (type Bar) as type Base in append
. When I use the empty Symbol
interface, everything works. However this approach completely bypasses the type system as now everything can be stored in the list. I would like to somehow denote that only Base
, Foo
and Bar
can be stored in the list, so that compiler can check if this rquirement is met. My structs do not have any methods, for sure not the ones that share behavior and can be added to the interface. Adding some dummy method to the interface and dummy implementations seems very artificial. What is the Go idiomatic way of handling such scenarios?
答案1
得分: 1
重要的是相同的接口:
package main
import (
"fmt"
)
type Item interface{
fmt.Stringer
}
type ItemA struct {
}
func (a ItemA) String() string {
return "物品A"
}
type ItemB struct {
}
func (a ItemB) String() string {
return "物品B"
}
func main() {
items := make([]Item, 0)
items = append(items, ItemA{}, ItemB{})
fmt.Printf("物品: %v", items)
}
英文:
What matters is the same interface:
package main
import (
"fmt"
)
type Item interface{
fmt.Stringer
}
type ItemA struct {
}
func (a ItemA) String() string {
return "item A"
}
type ItemB struct {
}
func (a ItemB) String() string {
return "item B"
}
func main() {
items := make([]Item, 0)
items = append(items, ItemA{}, ItemB{})
fmt.Printf("Items: %v", items)
}
答案2
得分: 1
你似乎期望的是"子类型多态性"。Go语言不支持非接口类型的动态绑定。因此,你可以使用接口来实现:
package main
func main() {
var bar Bar
var foo Foo
foo.Symbols = append(foo.Symbols, bar)
}
type Symbol interface {
Symbol()
}
type Base struct {
a int32
}
func (b Base) Symbol() {}
type Foo struct {
Base
b int32
Symbols []Symbol
}
type Bar struct {
Base
c int32
}
或者,如果你不喜欢使用接口,可以使用反射的技巧,像下面这样:
package main
import "fmt"
import "reflect"
func main() {
var bar Bar
var foo Foo
err := foo.AddSymbol(bar)
if err != nil {
fmt.Println(err)
}
}
type Base struct {
a int32
}
func (b Base) Symbol() {}
type Foo struct {
Base
b int32
symbol []interface{} // 将字段设为私有
}
// AddSymbol: 辅助函数用于追加值
func (f *Foo) AddSymbol(in interface{}) (err error) {
if f.symbol == nil {
f.symbol = make([]interface{}, 0)
}
switch typ := in.(type) {
case Base, Bar, Foo:
f.symbol = append(f.symbol, in)
default:
return fmt.Errorf("提供的类型:%s 不受支持", typ)
}
return nil
}
type Bar struct {
Base
c int32
}
英文:
What you seem to be expecting is subtype polymorphism
. Go does not support dynamic binding for non-interface types. Therefore you you could either use interfaces
package main
func main(){
var bar Bar
var foo Foo
foo.Symbols = append(foo.Symbols, bar)
}
type Symbol interface {
Symbol()
}
type Base struct {
a int32
}
func (b Base)Symbol(){}
type Foo struct {
Base
b int32
Symbols []Symbol
}
type Bar struct {
Base
c int32
}
or if you dislike using interfaces, you can use a trick like below with reflect.
package main
import "fmt"
func main(){
var bar Bar
var foo Foo
err := foo.AddSymbol(bar)
if err != nil{
fmt.Println(err)
}
}
type Base struct {
a int32
}
func (b Base)Symbol(){}
type Foo struct {
Base
b int32
symbol []interface{} // field made private
}
// AddSymbol : helper to append values
func (f *Foo)AddSymbol(in interface{})(err error){
if f.symbol == nil{
f.symbol = make([]interface{},0)
}
switch typ := in.(type) {
case Base,Bar,Foo:
f.symbol = append(f.symbol,in)
default:
return fmt.Errorf("provided type: %s is not supported",typ)
}
return nil
}
type Bar struct {
Base
c int32
}
答案3
得分: 1
我进行了一些搜索和阅读。我想要实现的目标需要所谓的“和类型”。目前,Go语言不支持和类型。然而,有几种替代方法可以模拟和类型的行为。在这里Alternatives to sum types in Go中对它们进行了很好的描述。
此外,看起来和类型可能会在Go 2中得到支持。更多阅读材料请参考proposal: spec: add sum types / discriminated unions和spec: generics: use type sets to remove type keyword in constraints。
英文:
I did some searching and reading. What I want to achieve requires so called "sum types". Currently Go does not support sum types. However, there are few alternative to simulate the behavior of sum types. They are quite nicely described here Alternatives to sum types in Go.
What is more, it looks like sum types will probably be supported in the Go 2. Further reading proposal: spec: add sum types / discriminated unions, spec: generics: use type sets to remove type keyword in constraints.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论