可变参数这么玩,装逼指南!Go语言可变参数技巧解密

简介: 可变参数这么玩,装逼指南!Go语言可变参数技巧解密

/ Go 语言可变参数指南 /

可变参数是 Go 语言一个非常实用的特性,可以实现函数接收可变个数的参数。本文将全面介绍可变参数的用法。本文主要内容如下

  1. 可变参数函数定义
  2. 调用可变参数函数
  3. 可变参数为 slice 类型
  4. 与普通 slice 参数的区别
  5. 固定参数与可变参数组合使用
  6. 多个可变参数函数的错误用法
  7. nil 可变参数的错误用法
  8. 反射获取可变参数信息
  9. 与接口类型可变参数的行为差异
  10. 错误的使用方式
  11. 实际应用场景
  12. 其他语言的可变参数
  13. 语言设计的取舍


1

 

一、可变参数函数定义

在函数参数列表中,可以使用省略号...来标识一个可变参数:

func myFunc(arg1 int, arg2 string, args ...float64) {}

这里args是一个可变参数,它可以接收零个或多个float64类型的参数。


2

 

二、调用可变参数函数

调用一个可变参数函数时,有两种传参方式:

  1. 按位置传参
myFunc(1, "hello") // 不传递可变参数 
myFunc(1, "hello", 1.1, 2.2) // 传递2个可变参数
  1. 通过 args 参数名传参
myFunc(1, "hello", args...[1.1, 2.2, 3.3])

可变参数既支持位置传参,也支持名称传参。


3

 

三、可变参数为 slice 类型

在函数内部,可变参数作为一个切片([]T)使用:

func myFunc(args ...int) {
  fmt.Printf("可变参数类型:%T\n", args) // []int
}

可以通过 range 遍历可变参数 slice:

func sum(values ...int) int {
  total := 0
  for _, val := range values {
    total += val
  }
  return total
}

也可以直接访问 slice 元素:values[0]


4

 

四、与普通 slice 参数的区别

可变参数函数其实就是语法糖,更优雅地支持了 slice 参数:

// 可变参数函数  
func sum(nums ...int) {}
// 对应普通slice参数
func sum(nums []int) {} 

可变参数比直接的 slice 参数更加简洁、直观。


5

 

五、固定参数与可变参数组合使用

可变参数非常灵活,可以与固定参数组合使用:

func concat(sep string, args ...string) {}

同时接受固定长度参数和可变参数。

调用时,可以按位置传参:

concat("-", "a", "b", "c")

或者通过 args 关键字传参:

concat("-", args...["a", "b", "c"])


6

 

六、多个可变参数函数的错误用法

需要注意,一个函数只能定义一个可变参数:

func myfunc(arg1 ...string, arg2 ...int) {} // 错误,不能定义多个可变参数

这是因为多个可变参数无法区分参数边界。

正确的定义方式:

func myfunc(arg1 ...interface{}) {} // 一个可变参数


7

 

七、nil 可变参数的错误用法

传入的可变参数不能为 nil 切片:

func myfunc(arg ...int) {}
myfunc(nil) // 错误,不能传入nil

    这是因为 nil 切片的元素类型是不确定的。


    8

     

    八、反射获取可变参数信息

    通过反射,可以获取可变参数的类型信息:

    import "reflect"
    func myfunc(args ...interface{}) {
      t := reflect.TypeOf(args) // []interface{}
      fmt.Println(t)  
    }

    这样在运行时就可以推断出实际的可变参数类型。


    9

     

    九、与接口类型可变参数的行为差异

    如果可变参数是接口类型,与普通类型会有一些差异:

    func myfunc(args ...interface{}) {
      for _, arg := range args {
        fmt.Println(arg) // 每个参数都是接口类型 
      }
    }

    需要进行类型断言来获取实际类型。


    10

     

    十、错误的使用方式

    以下是一些错误的可变参数使用方式:

    • 定义多个可变参数
    • 可变参数不能为 nil
    • 类型不匹配
    • 可变参数必须在最后

    需要注意避免这类错误用法。


    11

     

    十一、实际应用场景

    可变参数特别适合以下几种场景:

    • 不确定参数个数的函数
    • 格式化输出函数如 fmt.Printf
    • 列表处理函数如 strings.Join
    • 最大值最小值等统计类函数
    • 异常记录处理函数

    等等。


    12

     

    十二、其他语言的可变参数

    12.1

     

    C 语言

    C 语言使用va_listva_start、va_end处理可变参数:

    void print(int n,...) {
      va_list args;
      va_start(args, n);
      for(int i = 0; i < n; i++) {
        int arg = va_arg(args, int);
        printf("%d\n", arg);
      }
      va_end(args); 
    }

    稍微繁琐,不如 Go 语言优雅简洁。

    12.2

     

    Java

    Java 使用三点运算符表示可变参数:

    void print(String... args) {
    } 

    但统一隐式转换为 Object 数组,存在类型安全隐患。

    12.3

     

    Python

    Python 使用*args表示位置可变参数:


    def print(*args):

    但 args 为元组,类型不确定。

    12.4

     

    JavaScript

    JS 函数没有类型定义,所有参数本质上都是可变参数。


    13

     

    十三、语言设计的取舍

    可变参数有利有弊,语言设计需要做出取舍:

    13.1

     

    优点

    • 使用简单统一
    • 灵活方便
    • 类型安全

    13.2

     

    缺点

    • 参数类型隐藏
    • 调试不易
    • 性能损耗


    14

     

    总结

    Go 语言可变参数功能强大,同时又不失简洁与类型安全。正确合理使用可变参数函数,可以大大提升代码质量和开发效率。


    目录
    相关文章
    |
    3天前
    |
    监控 算法 Go
    Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
    【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
    26 0
    |
    23小时前
    |
    JavaScript 前端开发 Go
    Go语言的入门学习
    【4月更文挑战第7天】Go语言,通常称为Golang,是由Google设计并开发的一种编程语言,它于2009年公开发布。Go的设计团队主要包括Robert Griesemer、Rob Pike和Ken Thompson,这三位都是计算机科学和软件工程领域的杰出人物。
    7 1
    |
    1天前
    |
    Go
    |
    1天前
    |
    分布式计算 Java Go
    Golang深入浅出之-Go语言中的分布式计算框架Apache Beam
    【5月更文挑战第6天】Apache Beam是一个统一的编程模型,适用于批处理和流处理,主要支持Java和Python,但也提供实验性的Go SDK。Go SDK的基本概念包括`PTransform`、`PCollection`和`Pipeline`。在使用中,需注意类型转换、窗口和触发器配置、资源管理和错误处理。尽管Go SDK文档有限,生态系统尚不成熟,且性能可能不高,但它仍为分布式计算提供了可移植的解决方案。通过理解和掌握Beam模型,开发者能编写高效的数据处理程序。
    128 1
    |
    1天前
    |
    算法 关系型数据库 MySQL
    Go语言中的分布式ID生成器设计与实现
    【5月更文挑战第6天】本文探讨了Go语言在分布式系统中生成全局唯一ID的策略,包括Twitter的Snowflake算法、UUID和MySQL自增ID。Snowflake算法通过时间戳、节点ID和序列号生成ID,Go实现中需处理时间回拨问题。UUID保证全局唯一,但长度较长。MySQL自增ID依赖数据库,可能造成性能瓶颈。选择策略时需考虑业务需求和并发、时间同步等挑战,以确保系统稳定可靠。
    106 0
    |
    1天前
    |
    缓存 NoSQL Go
    Go语言中的分布式锁实现与选型
    【5月更文挑战第6天】本文探讨了Go语言中分布式锁的实现,包括Redis、ZooKeeper和Etcd三种方式,强调了选型时的性能、可靠性和复杂度考量。通过代码示例展示了Redis分布式锁的使用,并提出了避免死锁、公平性等问题的策略。结论指出,开发者应根据业务需求选择合适实现并理解底层原理,以确保系统稳定和高效。
    124 0
    |
    1天前
    |
    NoSQL 算法 Go
    Go语言中的分布式事务处理方案
    【5月更文挑战第6天】本文探讨了Go语言在分布式事务处理中的应用,包括2PC、3PC和TCC协议。通过示例展示了如何使用Go的`goroutine`和`channel`实现2PC。同时,文章指出了网络延迟、单点故障、死锁和幂等性等常见问题,并提供了相应的解决策略。此外,还以Redis Redlock为例,展示了如何实现分布式锁。理解并实施这些方案对于构建高可用的分布式系统至关重要。
    93 0
    |
    2天前
    |
    缓存 测试技术 持续交付
    Golang深入浅出之-Go语言中的持续集成与持续部署(CI/CD)
    【5月更文挑战第5天】本文介绍了Go语言项目中的CI/CD实践,包括持续集成与持续部署的基础知识,常见问题及解决策略。测试覆盖不足、版本不一致和构建时间过长是主要问题,可通过全面测试、统一依赖管理和利用缓存优化。文中还提供了使用GitHub Actions进行自动化测试和部署的示例,强调了持续优化CI/CD流程以适应项目需求的重要性。
    40 1
    |
    2天前
    |
    Kubernetes Cloud Native Go
    Golang深入浅出之-Go语言中的云原生开发:Kubernetes与Docker
    【5月更文挑战第5天】本文探讨了Go语言在云原生开发中的应用,特别是在Kubernetes和Docker中的使用。Docker利用Go语言的性能和跨平台能力编写Dockerfile和构建镜像。Kubernetes,主要由Go语言编写,提供了方便的客户端库与集群交互。文章列举了Dockerfile编写、Kubernetes资源定义和服务发现的常见问题及解决方案,并给出了Go语言构建Docker镜像和与Kubernetes交互的代码示例。通过掌握这些技巧,开发者能更高效地进行云原生应用开发。
    37 1
    |
    2天前
    |
    负载均衡 监控 Go
    Golang深入浅出之-Go语言中的服务网格(Service Mesh)原理与应用
    【5月更文挑战第5天】服务网格是处理服务间通信的基础设施层,常由数据平面(代理,如Envoy)和控制平面(管理配置)组成。本文讨论了服务发现、负载均衡和追踪等常见问题及其解决方案,并展示了使用Go语言实现Envoy sidecar配置的例子,强调Go语言在构建服务网格中的优势。服务网格能提升微服务的管理和可观测性,正确应对问题能构建更健壮的分布式系统。
    23 1