返回

Go字符串、函数与方法常见误区及更优方案

后端

  1. 直接比较字符串
if s1 == s2 {
  // do something
}

这种写法会直接比较字符串的内存地址,而不是其值。如果两个字符串的值相同,但存储在不同的内存地址中,则该比较将返回false。

解决方案:使用strings.Equal()函数比较字符串的值。

if strings.Equal(s1, s2) {
  // do something
}

41. 使用==!=比较字符串切片

if s1[i:j] == s2[i:j] {
  // do something
}

这种写法会直接比较字符串切片的内存地址,而不是其值。如果两个字符串切片的值相同,但存储在不同的内存地址中,则该比较将返回false。

解决方案:使用bytes.Equal()函数比较字符串切片的字节值。

if bytes.Equal(s1[i:j], s2[i:j]) {
  // do something
}

42. 在循环中连接字符串

var s string
for i := 0; i < n; i++ {
  s += "hello"
}

这种写法会随着循环次数的增加而导致字符串的副本不断被创建和销毁,这会降低程序的性能。

解决方案:使用strings.Builder来高效地连接字符串。

var b strings.Builder
for i := 0; i < n; i++ {
  b.WriteString("hello")
}
s := b.String()

43. 使用len()函数获取字符串的长度

fmt.Println(len(s))

这种写法会返回字符串的字节长度,而不是字符长度。如果字符串包含多字节字符,则其字符长度可能与字节长度不同。

解决方案:使用utf8.RuneCountInString()函数获取字符串的字符长度。

fmt.Println(utf8.RuneCountInString(s))

44. 使用fmt.Sprintf()函数格式化字符串

fmt.Printf("Hello, %s!", name)

这种写法会将name变量的值直接插入格式化字符串中,这可能导致安全漏洞。攻击者可以构造恶意输入,在格式化字符串中注入恶意代码。

解决方案:使用html.EscapeString()函数对name变量的值进行转义,以防止注入攻击。

fmt.Printf("Hello, %s!", html.EscapeString(name))

45. 使用reflect.DeepEqual()函数比较两个结构体

if reflect.DeepEqual(s1, s2) {
  // do something
}

这种写法会比较两个结构体的内存地址,而不是其值。如果两个结构体的值相同,但存储在不同的内存地址中,则该比较将返回false。

解决方案:使用自定义的比较函数来比较两个结构体的值。

func Equal(s1, s2 MyStruct) bool {
  return s1.Field1 == s2.Field1 &&
    s1.Field2 == s2.Field2 &&
    s1.Field3 == s2.Field3
}

if Equal(s1, s2) {
  // do something
}

46. 使用make()函数创建数组

var arr = make([]int, 10)

这种写法会创建一个具有10个元素的数组,但元素的初始值都是0。如果需要创建数组并为其元素指定初始值,则可以使用[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}这样的语法。

解决方案:使用[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}这样的语法创建数组,以指定元素的初始值。

47. 使用make()函数创建映射

var m = make(map[string]int)

这种写法会创建一个空映射,其中没有任何键值对。如果需要创建映射并为其添加键值对,则可以使用map[string]int{"key1": 1, "key2": 2, "key3": 3}这样的语法。

解决方案:使用map[string]int{"key1": 1, "key2": 2, "key3": 3}这样的语法创建映射,以添加键值对。