Golang中如何优雅地创建包含混合元素的结构体

Golang中如何优雅地创建包含混合元素的结构体 Pascal 有一种使用联合(union)创建变体类型结构体的方法。 在 C++ 中,你可以通过具有共同祖先的结构体/对象来实现这一点。 但到目前为止,我还没有找到不使用不安全指针和类型转换的解决方案。我是不是遗漏了什么技巧?

8 回复

你能提供一个你想解决的问题的例子吗? 根据具体的例子,你可以使用接口、嵌入或指针转换。

// 代码示例将根据具体问题在此处展示

更多关于Golang中如何优雅地创建包含混合元素的结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


查阅 Thorsten Ball 的《用 Go 语言编写解释器》(https://interpreterbook.com/)可能会很有帮助。我与 Thorsten 没有任何关联,但这本书看起来很有趣。

我知道这个解决方案。但我希望能找到一个更好的解决方案。在某些时候,语言维护者将不得不考虑C语言的解决方案,或者面向对象编程,或者一个不那么死板、带有返回值的接口模型。他们至少从2017年就开始就这个主题进行辩论了。这真令人心碎。

看来我不得不继续探索其他编程语言了。

无论如何,谢谢。

如果单个列表中的所有元素都具有相同的类型,可以使用泛型,例如:list[string]。如果同一列表中的元素可能具有不同的类型,可以直接使用接口类型“any”。

接口类型在数据足够小时会内联存储数据,如果数据较大,则存储指针。

对于非常特殊的用例,可以使用类似联合的数据类型配合指针转换,但我建议仅在现实条件下所有更安全/更简单的选项都无法满足需求时才考虑使用。

我正在尝试编写一个解释器。我需要实现一个包含不同类型元素的链表,例如字符串 | 数字 | 表达式列表。这是我学习新语言时的练习。

我参考了以下程序:https://github.com/pliba/kamin

到目前为止,我找到了这个:https://www.jerf.org/iri/post/2917/

但与C语言版本相比,它不够紧凑。

Francoys_Proulx:

在某些时候,语言维护者将不得不考虑 C 语言的解决方案,或者面向对象编程,或者一个不那么严格、带有返回值的接口模型。

Go 语言因其固执己见且与其他语言略有不同而闻名。如果它不适合您的用例,还有很多其他非常优秀的选择。但这种语言/工具链的严格性,大多数人将其视为一种特性。这有点像因为不喜欢借用检查器而抱怨 Rust,而借用检查器正是 Rust 的卖点之一。

Francoys_Proulx:

他们至少从 2017 年就开始讨论这个话题了。这真令人心碎。

这正是 Go 语言的优缺点之一。语言/编译器设计中的几乎所有事情都是一系列权衡。Go 团队行动缓慢,试图做出正确的权衡,并恪守其兼容性承诺。

Francoys_Proulx:

看来我不得不继续探索其他编程语言了。

如果您还没看过,不妨了解一下 zig

它似乎比 Rust 稍微简单一点。通常,我使用 Ruby。因为我更喜欢将精力集中在想法上,而不是如何构建想法上。我更像是分析师,而不是程序员。而且我主要做原型开发。但使用 Ruby 时,很容易窃取一个原型,并用它来制作成品。

Pascal 是我的第一门语言。Golang 与它有很多相似之处,并且速度足以满足我的需求。

我找到了问题的解决方案。

package main

import "fmt"

type Exptype int

const (
	NullType Exptype = iota
	IntType
	StringType
	ListType
)

type MixedNode struct {
	data interface{}
	next *MixedNode
}

// Returns an initialized list
func (n *MixedNode) Init() *MixedNode {
	n.data = -1
	return n
}

// Returns an new list
func New() *MixedNode {
	return new(MixedNode).Init()
}

// Returns the first node in list
func (n *MixedNode) Next() *MixedNode {
	return n.next
}

// Returns the last node in list if exist, otherwise returns current
func (n *MixedNode) Back() *MixedNode {
	current := n.next
	for current != nil && current.next != nil {
		current = current.next
	}
	return current
}

// This function prints contents of linked list starting from the given node
func printList(n *MixedNode) {
	for n != nil {
		fmt.Println(n.data)
		n = n.next
	}
}

func main() {
	//To allocate dynamically a new MixedNode in C language : head = (struct MixedNode*) malloc(sizeof(struct MixedNode));
	head := New()
	second := New()
	third := New()

	//Assign data in first node
	head.data = 1
	//Link first node with second node
	head.next = second

	second.data = "Allo"
	second.next = third

	nums := [4]int{1, 2, 3, 4} // init
	third.data = nums
	third.next = nil

	printList(head)
}

在Go语言中,可以通过接口(interface)和类型断言(type assertion)来优雅地创建包含混合元素的结构体,无需使用不安全指针。以下是一个示例:

package main

import (
	"fmt"
)

// 定义接口
type Element interface {
	Display()
}

// 定义不同类型实现接口
type Integer struct {
	Value int
}

func (i Integer) Display() {
	fmt.Printf("Integer: %d\n", i.Value)
}

type String struct {
	Value string
}

func (s String) Display() {
	fmt.Printf("String: %s\n", s.Value)
}

type Float struct {
	Value float64
}

func (f Float) Display() {
	fmt.Printf("Float: %.2f\n", f.Value)
}

// 包含混合元素的结构体
type Container struct {
	Elements []Element
}

func main() {
	// 创建包含不同类型元素的容器
	container := Container{
		Elements: []Element{
			Integer{Value: 42},
			String{Value: "Hello"},
			Float{Value: 3.14},
			Integer{Value: 100},
		},
	}

	// 遍历并显示所有元素
	for _, elem := range container.Elements {
		elem.Display()
	}

	// 类型断言示例
	for _, elem := range container.Elements {
		switch v := elem.(type) {
		case Integer:
			fmt.Printf("Processing Integer: %d\n", v.Value)
		case String:
			fmt.Printf("Processing String: %s\n", v.Value)
		case Float:
			fmt.Printf("Processing Float: %.2f\n", v.Value)
		}
	}
}

输出:

Integer: 42
String: Hello
Float: 3.14
Integer: 100
Processing Integer: 42
Processing String: Hello
Processing Float: 3.14
Processing Integer: 100

如果需要更紧凑的变体类型,可以使用带标签的联合结构:

package main

import (
	"fmt"
)

type Variant struct {
	Type  string
	Value interface{}
}

func main() {
	variants := []Variant{
		{"int", 42},
		{"string", "hello"},
		{"float64", 3.14},
		{"bool", true},
	}

	for _, v := range variants {
		switch v.Type {
		case "int":
			fmt.Printf("Integer: %d\n", v.Value.(int))
		case "string":
			fmt.Printf("String: %s\n", v.Value.(string))
		case "float64":
			fmt.Printf("Float: %.2f\n", v.Value.(float64))
		case "bool":
			fmt.Printf("Boolean: %v\n", v.Value.(bool))
		}
	}
}

对于需要类型安全的场景,可以使用泛型(Go 1.18+):

package main

import (
	"fmt"
)

type Variant[T any] struct {
	value T
}

func NewVariant[T any](value T) Variant[T] {
	return Variant[T]{value: value}
}

func (v Variant[T]) Get() T {
	return v.value
}

func main() {
	// 创建不同类型的变体
	intVariant := NewVariant(42)
	strVariant := NewVariant("hello")
	floatVariant := NewVariant(3.14)

	// 获取值(类型安全)
	fmt.Printf("Integer: %d\n", intVariant.Get())
	fmt.Printf("String: %s\n", strVariant.Get())
	fmt.Printf("Float: %.2f\n", floatVariant.Get())
}

这些方法提供了类型安全且优雅的方式来处理混合类型元素,避免了使用unsafe.Pointer和显式类型转换。

回到顶部