DevCN
菜单
全部文章快讯开发科技深度热点

Go 泛型入门:用类型参数复用查找逻辑

实现一个既能查整数又能查字符串的函数,理解类型参数、可比较约束和未找到时的返回约定。


本节目标

实现一个既能查整数又能查字符串的函数,理解类型参数、可比较约束和未找到时的返回约定。

前置知识

已理解函数、切片和相等比较。泛型示例要求Go 1.18或更高版本;本批验证环境是Go 1.25.7。

准备与运行

在一个新的练习目录执行以下命令。目录名可以修改;若该目录已经初始化过模块,不要重复执行go mod init。

mkdir go-lesson-09
cd go-lesson-09
go mod init example.com/devcn/lesson09

将下方完整代码保存为当前目录的main.go,然后执行:

go fmt ./...
go run .

先用go version确认工具链可用。本例在Go 1.25.7、macOS ARM64环境验证,只使用标准库;没有测试所有操作系统和Go版本。

核心概念

普通函数可以把重复的操作封装起来,但如果输入元素类型不同,容易出现两个几乎相同的实现。find把元素类型提为参数T,循环结构保留一份。

约束comparable使函数可以对T的值进行相等比较。不是任何类型都能比较,例如普通切片不能使用==比较两个切片是否内容相同,所以不能直接拿[]int作为这个函数的元素类型。

本例调用时省略显式类型实参,编译器根据传入的切片和目标值推断。泛型并不会让Go变成运行时随意混用类型的语言,不满足约束的调用仍会被编译检查。

完整示例

package main

import "fmt"

func find[T comparable](items []T, want T) int {
	for i, v := range items {
		if v == want {
			return i
		}
	}
	return -1
}
func main() { fmt.Println(find([]int{4, 8, 12}, 8)); fmt.Println(find([]string{"Go", "SQL"}, "Rust")) }

运行后应得到:

1
-1

代码如何工作

find按顺序检查元素,第一次匹配就返回索引。如果循环结束仍未匹配,返回-1。该约定需要调用方遵守:结果为-1时不能直接作为切片索引。

整数列表4、8、12中的8位于索引1;字符串列表中没有Rust,所以返回-1。若目标出现多次,本实现返回第一次出现的位置,而不是全部位置。

时间开销随扫描元素数量增长,额外空间保持很小。频繁重复查找很大的列表时,可以考虑预建索引,但不要为了短列表过早引入复杂结构。

常见错误与边界

  • -1是未找到的标记,不能直接执行items[-1]。
  • 泛型的价值在于类型不同而操作一致;只有一处使用时,普通函数可能更清楚。
  • comparable并不意味着可以排序。若要使用<或>,需要不同的约束设计。
  • 切片、Map等类型不能直接作为本例的普通可比较元素;比较复杂对象时应设计比较函数或可比较的键。

练习

用find实现Contains,并验证空整数切片、存在的字符串和不存在的字符串。

参考答案

新增函数:

func Contains[T comparable](items []T, want T) bool {
    return find(items, want) >= 0
}

Contains([]int{}, 1)得到false;Contains([]string{"Go"}, "Go")得到true;查找Rust得到false。这里将“索引是否有效”集中处理,调用方只要关心是否存在即可。

DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。