Go 语言大白话入门 7 - 函数:把重复代码收起来

函数:把重复代码收起来

本章目标:把重复逻辑收进函数里,做到三件事就算过关:会写函数、会传参数、会接返回值。

常见坑位

  • 代码还没重复,就提前拆一堆函数,最后自己都看不懂。
  • 函数写好了,但忘了在 main 里调用,运行后啥都没发生。
  • 参数和返回值方向搞反:把"输入"写成"输出"。
  • 看见多返回值就紧张,其实就是"一个函数一次带回多个结果"。
  • 误以为函数里改了变量,外面一定会变。Go 默认是值传递,不一定会变。

先跑一个最小示例

先跑起来,再理解细节,这是最省时间的学习方式。

go 复制代码
package main

import "fmt"

func sayHello() {
    fmt.Println("你好,欢迎学习 Go")
}

func main() {
    sayHello()
    sayHello()
}

看到两行输出就说明你已经会"定义 + 调用"函数了。

带参数的函数

参数可以理解成"你喂给函数的数据"。

go 复制代码
package main

import "fmt"

func sayHi(name string) {
    fmt.Println("你好,", name)
}

func main() {
    sayHi("小张")
    sayHi("小刘")
}

这里 name string 的意思是:这个函数接收一个字符串类型的名字。

带返回值的函数

返回值就是"函数做完活后交回来的结果"。

go 复制代码
package main

import "fmt"

func add(a int, b int) int {
    return a + b
}

func main() {
    result := add(3, 5)
    fmt.Println(result) // 8
}

你可以把 add 理解成一个小工具:给它两个数,它回你一个和。

多返回值:Go 的高频写法

Go 很常见的一个特点是:一个函数可以返回多个值。

go 复制代码
package main

import "fmt"

func divide(a int, b int) (int, string) {
    if b == 0 {
        return 0, "不能除以零"
    }
    return a / b, ""
}

func main() {
    result, msg := divide(10, 2)
    fmt.Println(result, msg) // 5

    result2, msg2 := divide(10, 0)
    fmt.Println(result2, msg2) // 0 不能除以零
}

后面你会大量见到这种形式:

go 复制代码
value, err := someFunc()

前面是结果,后面是错误信息。下一章我们专门讲这个模式。

变长参数:参数个数不固定

当你不确定要传几个参数时,用 ...

go 复制代码
package main

import "fmt"

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

func main() {
    fmt.Println(sum(1, 2, 3))         // 6
    fmt.Println(sum(10, 20, 30, 40))  // 100
}

nums ...int 在函数内部本质上就是 []int,这点后面学切片时会越用越顺。

匿名函数:临时用一次,不想起名

匿名函数就是"没有名字的函数"。

go 复制代码
package main

import "fmt"

func main() {
    greet := func(name string) {
        fmt.Println("你好,", name)
    }

    greet("小张")
    greet("小李")

    func() {
        fmt.Println("这段逻辑只执行一次")
    }()
}

以后在排序、HTTP 处理、goroutine 里,你会反复看到匿名函数。

闭包:匿名函数能记住外面的变量

go 复制代码
package main

import "fmt"

func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    next := counter()
    fmt.Println(next()) // 1
    fmt.Println(next()) // 2
    fmt.Println(next()) // 3
}

这里返回的匿名函数记住了 count,这就是闭包。

初学先记两点:

  • 闭包可以访问外层变量。
  • 闭包会让外层变量活得更久,别长期挂大对象。

函数还能当参数传来传去

Go 里函数也是"值",可以传给别的函数。

go 复制代码
package main

import "fmt"

func apply(nums []int, fn func(int) int) []int {
    result := make([]int, 0, len(nums))
    for _, n := range nums {
        result = append(result, fn(n))
    }
    return result
}

func main() {
    doubled := apply([]int{1, 2, 3}, func(n int) int {
        return n * 2
    })
    fmt.Println(doubled) // [2 4 6]
}

fn func(int) int 的意思是:这个参数本身是一个函数,输入 int,输出 int

值传递和引用类型,一次讲清

Go 只有值传递。

  • intstring、结构体:函数里改变量,外面通常不变。
  • slicemap:拷贝的是结构头,但底层数据共享,改元素外面能看到。

所以"函数里改了,外面会不会变",要看你传入的具体类型。

过关标准

你可以按这份清单自检:

  • 能自己写出并调用一个无参数函数。
  • 能写一个带参数函数,并传入不同数据。
  • 能写一个带返回值函数,并正确接收结果。
  • 看到 value, err := xxx() 不再慌。
  • 能说清"值传递"和"slice/map 的表现差异"。

练习任务

  1. 写一个 welcome(),打印一句欢迎语。
  2. 写一个 sayHi(name string),分别给两个名字打招呼。
  3. 写一个 max(a, b int) int,返回较大的那个数。
  4. 写一个 sum(nums ...int) int,输入任意个整数并求和。

下一章衔接

下一章我们就接着讲 Go 里最常见的错误处理模式:value, err := ... 到底该怎么写才稳。

本文章属于《Go语言大白话入门》零基础全套教程,持续更新、全程实操练手。 微信公众号【码栈研说】超前更新,目前比掘金领先 30 章内容,完整连载、无删减、持续日更,需要完整系统学习的读者可自行前往查看。

文中部分内容借助 AI 辅助梳理,全文人工校验、实操落地。

相关推荐
步行cgn8 分钟前
传统 SSM 与 Spring Boot 开发对比:从配置地狱到约定优于配置
java·spring boot·后端
JavaGuide2 小时前
一个 SKILL.md 拿下 2.3 万 Star:让 Codex/Claude Code 少说废话、先给答案
前端·后端
陈随易9 小时前
pm2 替代品,nodejs&bun 线上部署必备工具
前端·后端·程序员
丘山一郎10 小时前
Spring 中的IOC控制反转 和DI依赖注入
java·后端·spring
码视野10 小时前
基于 Spring Boot + Vue3 的【大学英语四六级 (CET-4/6) 作文智能评分与句式润色系统】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端·vue3
MetaLite10 小时前
Spring-AOP自调用为什么失效-AopContext真能解决吗
java·后端·spring
MC皮蛋侠客11 小时前
Tauri 2.x 系列(五):调用系统 API——官方插件、Rust crate 与原生能力
开发语言·后端·rust
Profile排查笔记13 小时前
指纹浏览器怎么设置 IP?代理配置、检测与排错流程
前端·人工智能·后端·自动化
IT_陈寒14 小时前
SpringBoot自动配置失效?你可能漏了这个小开关
前端·人工智能·后端
kyriewen14 小时前
我写了1个复盘Skill,和AI协作踩过的坑第二天自动变成护栏
前端·程序员·ai编程