学习 Golang 时需要注意的常见“陷阱”有哪些?

huangapple go评论68阅读模式
英文:

What are common "Go-tchas" that someone learning Golang should watch out for?

问题

例如,“Effective Go”文档中有以下条目:

像C语言一样,Go的正式语法使用分号来终止语句,但与C不同的是,这些分号在源代码中并不出现。相反,词法分析器在扫描时使用一个简单的规则自动插入分号,因此输入文本基本上没有分号。

为了简洁起见,省略了一些部分。

像这样编写

if i < f() {
    g()
}

而不是这样编写

if i < f()  // 错误!
{           // 错误!
    g()
}

执行错误版本会产生以下错误消息:

/tmp/test.go:6: if语句中缺少条件
/tmp/test.go:6: true被计算但未使用

在我看来,这两条消息都没有给程序员关于放置花括号错误的线索。如果我没有阅读上面的文档,将来我可能会使用错误的版本编写一些Go代码(我通常在编写使用花括号的代码时使用第一个版本),然后为什么缺少“if”条件而感到困惑。

Golang中还有其他需要注意的“陷阱”吗?

英文:

For example, the "Effective Go" documentation has the following entries:

> Like C, Go's formal grammar uses semicolons to terminate statements, but unlike in C, those semicolons do not appear in the source. Instead the lexer uses a simple rule to insert semicolons automatically as it scans, so the input text is mostly free of them.

Cut off some parts for brevity.

> Write them like this

if i &lt; f() {
    g()
}

> not like this

if i &lt; f()  // wrong!
{           // wrong!
    g()
}

The wrong version when executed produces the following error messages:

/tmp/test.go:6: missing condition in if statement
/tmp/test.go:6: true evaluated but not used

IMO, both messages don't give the coder a clue about misplaced curly braces. Had I failed to read the documentation above, I would have probably written some Go code in the future using the wrong version (I usually use the 1st version in writing code that uses curly braces), then banged my head as to why I'm missing an "if" condition when one is clearly present.

Are there other "gotchas" in Golang that I should be aware of?

答案1

得分: 7

这可能只是一个评论,但我将其发布为答案,以更加强调它,因为我确实认为这篇链接的文章非常好,即使对于初学者来说也非常有用,而且它仍然是未知的。

这是我见过的最好、最完整的:

50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

它涵盖了从完全初学者(例如“Opening Brace Can't Be Placed on a Separate Line”或“Unused Imports”)到高级初学者(例如“nil”接口和“nil”接口值或“Preemptive Scheduling”)的主题。

因此,对于常见陷阱或“坑”,一个很好的摘要基本上就是它们的目录,你可以在那里阅读更多相关内容:

完全初学者:

  • 大括号不能放在单独的一行
  • 未使用的变量
  • 未使用的导入
  • 短变量声明只能在函数内部使用
  • 使用短变量声明重新声明变量
  • 意外的变量遮蔽
  • 不能使用“nil”来初始化没有明确类型的变量
  • 使用“nil”切片和映射
  • 映射容量
  • 字符串不能为“nil”
  • 数组函数参数
  • “range”子句中的切片和数组中的意外值
  • 切片和数组是一维的
  • 访问不存在的映射键
  • 字符串是不可变的
  • 字符串和字节切片之间的转换
  • 字符串和索引运算符
  • 字符串并不总是UTF8文本
  • 字符串长度
  • 多行切片/数组/映射文字中缺少逗号
  • log.Fatal和log.Panic不仅仅是日志
  • 内置数据结构操作不是同步的
  • 在“range”子句中的字符串迭代值
  • 使用“for range”子句迭代映射
  • “switch”语句中的fallthrough行为
  • 递增和递减
  • 按位非运算符
  • 运算符优先级差异
  • 未导出的结构字段未编码
  • 应用程序退出时存在活动的goroutine
  • 发送到无缓冲通道将在目标接收器准备好时立即返回
  • 发送到关闭的通道会引发恐慌
  • 使用“nil”通道
  • 值接收器的方法无法更改原始值

中级初学者:

  • 关闭HTTP响应体
  • 关闭HTTP连接
  • 将JSON数字解组为接口值
  • 比较结构体、数组、切片和映射
  • 从恐慌中恢复
  • 在切片、数组和映射的“for range”子句中更新和引用项目值
  • 切片中的“隐藏”数据
  • 切片数据损坏
  • “陈旧”的切片
  • 类型声明和方法
  • 从“for switch”和“for select”代码块中跳出
  • “for”语句中的迭代变量和闭包
  • 延迟函数调用参数评估
  • 延迟函数调用执行
  • 类型断言失败
  • 阻塞的goroutine和资源泄漏

高级初学者:

  • 在值实例上使用指针接收器方法

  • 更新映射值字段

  • “nil”接口和“nil”接口值

  • 栈和堆变量

  • GOMAXPROCS、并发和并行性

  • 读写操作重排序

  • 抢占式调度

英文:

<sup>This could've been just a comment, but posted as an answer to lay more emphasis on it, because I do believe the linked article is really good, useful even for non-starters and it is still unknown.</sup>


This is the best and most complete I've seen:

50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

It has topics ranging from total beginner (like "Opening Brace Can't Be Placed on a Separate Line" or "Unused Imports") to advanced beginner (like "nil" Interfaces and "nil" Interfaces Values or "Preemptive Scheduling").

So a nice summary of the common pitfalls or "gotchas" is basically their table of contents, all of which you can read more about there:

Total Beginner:

  • Opening Brace Can't Be Placed on a Separate Line
  • Unused Variables
  • Unused Imports
  • Short Variable Declarations Can Be Used Only Inside Functions
  • Redeclaring Variables Using Short Variable Declarations
  • Accidental Variable Shadowing
  • Can't Use "nil" to Initialize a Variable Without an Explicit Type
  • Using "nil" Slices and Maps
  • Map Capacity
  • Strings Can't Be "nil"
  • Array Function Arguments
  • Unexpected Values in Slice and Array "range" Clauses
  • Slices and Arrays Are One-Dimensional
  • Accessing Non-Existing Map Keys
  • Strings Are Immutable
  • Conversions Between Strings and Byte Slices
  • Strings and Index Operator
  • Strings Are Not Always UTF8 Text
  • String Length
  • Missing Comma In Multi-Line Slice/Array/Map Literals
  • log.Fatal and log.Panic Do More Than Log
  • Built-in Data Structure Operations Are Not Synchronized
  • Iteration Values For Strings in "range" Clauses
  • Iterating Through a Map Using a "for range" Clause
  • Fallthrough Behavior in "switch" Statements
  • Increments and Decrements
  • Bitwise NOT Operator
  • Operator Precedence Differences
  • Unexported Structure Fields Are Not Encoded
  • App Exits With Active Goroutines
  • Sending to an Unbuffered Channel Returns As Soon As the Target Receiver Is Ready
  • Sending to an Closed Channel Causes a Panic
  • Using "nil" Channels
  • Methods with Value Receivers Can't Change the Original Value

Intermediate Beginner:

  • Closing HTTP Response Body
  • Closing HTTP Connections
  • Unmarshalling JSON Numbers into Interface Values
  • Comparing Structs, Arrays, Slices, and Maps
  • Recovering From a Panic
  • Updating and Referencing Item Values in Slice, Array, and Map "for range" Clauses
  • "Hidden" Data in Slices
  • Slice Data Corruption
  • "Stale" Slices
  • Type Declarations and Methods
  • Breaking Out of "for switch" and "for select" Code Blocks
  • Iteration Variables and Closures in "for" Statements
  • Deferred Function Call Argument Evaluation
  • Deferred Function Call Execution
  • Failed Type Assertions
  • Blocked Goroutines and Resource Leaks

Advanced Beginner:

  • Using Pointer Receiver Methods On Value Instances

  • Updating Map Value Fields

  • "nil" Interfaces and "nil" Interfaces Values

  • Stack and Heap Variables

  • GOMAXPROCS, Concurrency, and Parallelism

  • Read and Write Operation Reordering

  • Preemptive Scheduling

huangapple
  • 本文由 发表于 2015年10月18日 12:49:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/33194409.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定