Golang中默认参数和嵌套结构体的推断问题
Golang中默认参数和嵌套结构体的推断问题 在使用Go和TypeScript体验Pulumi后,我对Go语言缺乏可选/默认参数以及无法推断嵌套类型的问题有一些思考,很想知道社区对此的看法。
以下是一个在Go和TypeScript中完全相同的Pulumi项目,用于在K8s中启动一个Nginx部署。
import * as k8s from "@pulumi/kubernetes";
const appLabels = { app: "nginx" };
const deployment = new k8s.apps.v1.Deployment("nginx", {
spec: {
selector: { matchLabels: appLabels },
replicas: 1,
template: {
metadata: { labels: appLabels },
spec: { containers: [{ name: "nginx", image: "nginx" }] }
}
}
});
export const name = deployment.metadata.name;
package main
import (
appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/apps/v1"
corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/core/v1"
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/meta/v1"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
appLabels := pulumi.StringMap{
"app": pulumi.String("nginx"),
}
deployment, err := appsv1.NewDeployment(ctx, "app-dep", &appsv1.DeploymentArgs{
Spec: appsv1.DeploymentSpecArgs{
Selector: &metav1.LabelSelectorArgs{
MatchLabels: appLabels,
},
Replicas: pulumi.Int(1),
Template: &corev1.PodTemplateSpecArgs{
Metadata: &metav1.ObjectMetaArgs{
Labels: appLabels,
},
Spec: &corev1.PodSpecArgs{
Containers: corev1.ContainerArray{
corev1.ContainerArgs{
Name: pulumi.String("nginx"),
Image: pulumi.String("nginx"),
}},
},
},
},
})
if err != nil {
return err
}
ctx.Export("name", deployment.Metadata.Elem().Name())
return nil
})
}
TypeScript版本清晰得多,以至于无法选择Go作为此类IaaC项目的后端语言。我希望有人看到代码时能立刻明白他们在看什么。
这里存在两个问题:
- Pulumi大量使用了可选参数的变通方法,因此你必须使用将值转换为引用的“技巧”,以便它可以为
nil。在我看来,这并不是糟糕的设计,而是一个希望拥有合理默认值的API的良好选择,这样你只需更改需要更改的部分。 - 嵌套结构体无法被推断,因此你必须为每个嵌套结构体命名,这在一个需要更长描述性结构体名称的大型项目中会导致大量杂乱。
这类情况非常普遍,例如使用像AWS这样的云服务提供商API时。我看到GCP在Go方面有专长,因此拥有更扁平的结构,并且设计方式上不需要使用可选参数的技巧,但像GKE这样的服务仍然存在深度嵌套且杂乱的结构体。
我非常喜欢Go的简洁性,我现在使用的几乎所有云工具都是用Go编写的,我也希望将它用于IaaC,但目前TypeScript是更好的选择。我认为这两点虽然给API设计者增加了一点复杂性,但却极大地降低了API使用者的复杂性。
更多关于Golang中默认参数和嵌套结构体的推断问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中默认参数和嵌套结构体的推断问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Go语言确实没有可选参数和默认参数的语言特性,这导致在需要处理大量可选字段的API(如云服务SDK)中,代码会显得冗长。不过,我们可以通过一些模式来改善这种情况。
对于嵌套结构体的推断问题,虽然Go不支持类型推断,但可以通过结构体字面量的方式减少类型重复。以下是一些改进示例:
1. 使用辅助函数简化可选字段
// 为常用类型创建辅助函数
func PtrInt(v int) *int { return &v }
func PtrString(v string) *string { return &v }
func PtrBool(v bool) *bool { return &v }
// 使用示例
deploymentArgs := &appsv1.DeploymentArgs{
Spec: &appsv1.DeploymentSpecArgs{
Replicas: PtrInt(1),
// 其他字段...
},
}
2. 使用构建器模式
type DeploymentBuilder struct {
args *appsv1.DeploymentArgs
}
func NewDeploymentBuilder(name string) *DeploymentBuilder {
return &DeploymentBuilder{
args: &appsv1.DeploymentArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: PtrString(name),
},
Spec: &appsv1.DeploymentSpecArgs{},
},
}
}
func (b *DeploymentBuilder) WithReplicas(replicas int) *DeploymentBuilder {
b.args.Spec.Replicas = PtrInt(replicas)
return b
}
func (b *DeploymentBuilder) Build() *appsv1.DeploymentArgs {
return b.args
}
// 使用示例
deploymentArgs := NewDeploymentBuilder("nginx").
WithReplicas(3).
Build()
3. 使用函数选项模式
type DeploymentOption func(*appsv1.DeploymentArgs)
func WithReplicas(replicas int) DeploymentOption {
return func(args *appsv1.DeploymentArgs) {
args.Spec.Replicas = PtrInt(replicas)
}
}
func WithLabels(labels map[string]string) DeploymentOption {
return func(args *appsv1.DeploymentArgs) {
if args.Metadata == nil {
args.Metadata = &metav1.ObjectMetaArgs{}
}
args.Metadata.Labels = labels
}
}
func NewDeployment(opts ...DeploymentOption) *appsv1.DeploymentArgs {
args := &appsv1.DeploymentArgs{
Spec: &appsv1.DeploymentSpecArgs{},
}
for _, opt := range opts {
opt(args)
}
return args
}
// 使用示例
deploymentArgs := NewDeployment(
WithReplicas(3),
WithLabels(map[string]string{"app": "nginx"}),
)
4. 简化嵌套结构体初始化
// 使用局部变量和类型推断
appLabels := pulumi.StringMap{"app": pulumi.String("nginx")}
deploymentArgs := &appsv1.DeploymentArgs{
Spec: &appsv1.DeploymentSpecArgs{
Selector: &metav1.LabelSelectorArgs{MatchLabels: appLabels},
Replicas: pulumi.Int(1),
Template: &corev1.PodTemplateSpecArgs{
Metadata: &metav1.ObjectMetaArgs{Labels: appLabels},
Spec: &corev1.PodSpecArgs{
Containers: corev1.ContainerArray{
{
Name: pulumi.String("nginx"),
Image: pulumi.String("nginx"),
},
},
},
},
},
}
这些模式虽然不能完全达到TypeScript的可选参数和类型推断效果,但可以显著改善Go代码的可读性和编写体验。对于Pulumi这类工具,建议SDK提供者考虑内置这些简化模式。

