当前位置:首页 > 科技  > 软件

从错误中学习:了解 Go 编程的六个坏习惯

来源: 责编: 时间:2024-01-08 09:16:12 317观看
导读使用Go和使用其他编程语言中一样,需要了解常见错误和不良实践,才能编写既干净又高效的代码。本文讨论的一些实践并不一定都是不好的,在特定情况下很有用。 然而,我们需要知道可能会有什么问题,为什么应该回避某些习惯,以及

使用Go和使用其他编程语言中一样,需要了解常见错误和不良实践,才能编写既干净又高效的代码。CwH28资讯网——每日最新资讯28at.com

本文讨论的一些实践并不一定都是不好的,在特定情况下很有用。 然而,我们需要知道可能会有什么问题,为什么应该回避某些习惯,以及如何避开常见的陷阱。CwH28资讯网——每日最新资讯28at.com

CwH28资讯网——每日最新资讯28at.com

1. 使用init()

Go中的init()函数是一个特殊函数,在main函数之前执行。CwH28资讯网——每日最新资讯28at.com

"如果初始化对于任何包都很重要,为什么init()在Go中被认为是一个不好的做法?"CwH28资讯网——每日最新资讯28at.com

CwH28资讯网——每日最新资讯28at.com

是的,虽然init()函数确实有助于在运行核心逻辑之前进行初始化,但其执行顺序可能很难理解,可能导致对初始化顺序的混淆。CwH28资讯网——每日最新资讯28at.com

// package Afunc init() {}// package B func init() {}// which run first?

想象一下,有两个模块在安装时相互依赖,但位于不同的包中。结果我们最终需要编写更复杂的代码来管理时序,更糟的是,甚至可能陷入死锁情况。CwH28资讯网——每日最新资讯28at.com

使用init()的另一个缺点是测试会变得复杂。因为这些函数是自动运行的,无法选择何时执行。CwH28资讯网——每日最新资讯28at.com

缺乏控制使得设置测试用例成为一项挑战。CwH28资讯网——每日最新资讯28at.com

我曾经遇到过一个问题,我的服务在部署后花了很长时间才准备好。我在main()函数的开始处设置了一个断点,但从未触发。CwH28资讯网——每日最新资讯28at.com

CwH28资讯网——每日最新资讯28at.com


CwH28资讯网——每日最新资讯28at.com

经过冗长的调试后,我们发现一个成员使用了某个包中的init()函数从一个大文件加载一个大数据集,这让我们花费大量时间去解决这么一个小问题。CwH28资讯网——每日最新资讯28at.com

2. 使用全局变量

Go中的全局变量可能会带来类似单例的问题,特别是当这些全局变量很复杂时(比如映射、切片或指针)。CwH28资讯网——每日最新资讯28at.com

"那么,全局变量有什么大不了的?"CwH28资讯网——每日最新资讯28at.com

CwH28资讯网——每日最新资讯28at.com

  • 竞争条件: 当有多个程序试图同时访问同一个全局变量时,事情可能会变得混乱。
  • 更少的可测试性: 应用程序依赖于全局变量,意味着有状态,从而在单元或集成测试期间,这些全局变量需要与main()函数中的内容或在生产环境中部署的内容保持一致。
  • 模块化程度较低,可重用性较差: 可以从任何地方访问全局变量,很难跟踪其使用方式和位置。

因此,这里的建议是保持对包的封装。CwH28资讯网——每日最新资讯28at.com

从而使得代码更容易移动,并且不太可能破坏其他东西。通过避免使用全局变量,可以使代码不那么受约束,并且更容易更新或复用。CwH28资讯网——每日最新资讯28at.com

3. 忽略错误信息

用Go编程时,错误是不可避免的,知道如何处理错误可以让我们避免各种各样的问题。CwH28资讯网——每日最新资讯28at.com

"忽略错误真的那么糟糕吗?"CwH28资讯网——每日最新资讯28at.com

CwH28资讯网——每日最新资讯28at.com

是的,完全正确。CwH28资讯网——每日最新资讯28at.com

一些Go新手可能会用"_"符号将错误撇在一边,但忽略函数返回的错误值,可能会带来麻烦。CwH28资讯网——每日最新资讯28at.com

如果不对错误进行管理,也许程序会出现panic和crash。CwH28资讯网——每日最新资讯28at.com

// sample 1func main() {  var x interface{} = "hello"  s := x.(int) // panic: interface conversion: interface {} is string, not int  fmt.Println(s)}// sample 2func main() {  var x interface{} = "hello"  s, _ := x.(int) // safe but DON'T  fmt.Println(s)}

跳过错误可能会适得其反,尤其是对于线上生产环境,调试会成为一场噩梦。总是--我的意思是总是--检查错误并采取正确的措施以保持代码顺利运行。CwH28资讯网——每日最新资讯28at.com

4. 避免GOTO

无论用Go还是其他语言,避免使用"goto"是大家的共识。CwH28资讯网——每日最新资讯28at.com

使用goto会破坏代码的自然流程。CwH28资讯网——每日最新资讯28at.com

会破坏我们理解不同代码段之间关系的方式,让我们很难在不弄得乱七八糟的情况下修改代码。CwH28资讯网——每日最新资讯28at.com

此外,调试也变得更加令人困惑,测试也更加棘手。CwH28资讯网——每日最新资讯28at.com

从本质上讲,依赖goto往往会产生更多错误,并难以深入了解问题。因此,作为最佳实践,明智的做法是避开它。CwH28资讯网——每日最新资讯28at.com

5. 跳过Defer和Recover

如果你忽略"defer"和"recover",就失去了对panic的坚实保护。CwH28资讯网——每日最新资讯28at.com

为什么?CwH28资讯网——每日最新资讯28at.com

因为当出现panic时,"defer"仍然会起作用,而"recover"会抓住panic,让我们有机会处理不可预见的问题[2]。CwH28资讯网——每日最新资讯28at.com

看看这个例子,其中'file.Close()'只是放在末尾,这不是一个Go风格的解决方案:CwH28资讯网——每日最新资讯28at.com

func readFile(filename string) {    file, err := os.Open(filename)    if err != nil {        panic(err)    }    // Do something with the file    file.Close() // <--- DONT}

相反,像这样使用"defer":CwH28资讯网——每日最新资讯28at.com

func readFile(filename string) {    file, err := os.Open(filename)    if err != nil {        panic(err)    }    defer file.Close()    // Do something with the file    ...}

在打开文件后立即调用defer file.Close()可以确保即使readFile()遇到panic,文件也会被关闭。此外,还可以方便的提醒我们在打开资源后立即进行清理。CwH28资讯网——每日最新资讯28at.com

6. 过多使用context.Background()

Go的context功能非常有用,当代码与数据库或网站对话时,有助于管理时间限制等事情。CwH28资讯网——每日最新资讯28at.com

如果没有设定截止时间,应用可能会陷入阻塞,被数以百万计的请求淹没。CwH28资讯网——每日最新资讯28at.com

通过一个特殊功能,可以很容易的设置时间限制。CwH28资讯网——每日最新资讯28at.com

该函数有三种时间选择: Fast(0.5秒)、Medium(3秒)和Slow(10秒)。这样就不用一直使用context.Background(),而且可以为每个任务选择合适的时间限制。CwH28资讯网——每日最新资讯28at.com

以下是Fast的一些示例代码:CwH28资讯网——每日最新资讯28at.com

const FastTimeout = 500 * time.Millisecondfunc WrapCustomContext(ctx context.Context, dur time.Duration) (context.Context, context.CancelFunc) {  return context.WithTimeout(ctx, dur)}func GenFastContext() (context.Context, context.CancelFunc) {  return WrapCustomContext(context.Background(), FastTimeout)}func WrapFastContext(ctx context.Context) (context.Context, context.CancelFunc) {  return WrapCustomContext(ctx, FastTimeout)}

有了这些函数,就可以选择正确的时间限制,应用也因此运行得更好。CwH28资讯网——每日最新资讯28at.com

好还是不好,只是一些概念,我们可以决定其真正含义。CwH28资讯网——每日最新资讯28at.com

所以,明智的使用"不好"的特性,它就能变成"最好"的方案。CwH28资讯网——每日最新资讯28at.com

参考资料:CwH28资讯网——每日最新资讯28at.com

  • [1]5+ BAD Practices In Go: Learn From Mistakes: https://levelup.gitconnected.com/5-bad-practices-in-go-learn-from-mistakes-13afb4d303b3
  • [2]What you know about defer in Go is not enough!: https://medium.com/@func25/what-you-know-about-defer-in-go-is-not-enough-2681d4b128c3

本文链接:http://www.28at.com/showinfo-26-57886-0.html从错误中学习:了解 Go 编程的六个坏习惯

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: C++控制台彩色时钟的实现

下一篇: 什么是Helm?它是如何提升云原生应用私有化部署效率的

标签:
  • 热门焦点
  • 2023年Q2用户偏好榜:12+256G版本成新主流

    3月份的性能榜、性价比榜和好评榜之后,就要轮到2023年的第二季度偏好榜了,上半年的新机潮已经过去,最明显的肯定就是大内存和存储的机型了,另外部分中端机也取消了屏幕塑料支架
  • 5月安卓手机好评榜:魅族20 Pro夺冠

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年5月1日至5月31日,仅限国内市场。第一名:魅族20 Pro好评率:97.50%不得不感慨魅族老品牌还
  • 把LangChain跑起来的三个方法

    使用LangChain开发LLM应用时,需要机器进行GLM部署,好多同学第一步就被劝退了,那么如何绕过这个步骤先学习LLM模型的应用,对Langchain进行快速上手?本片讲解3个把LangChain跑起来
  • 自动化在DevOps中的力量:简化软件开发和交付

    自动化在DevOps中扮演着重要角色,它提升了DevOps的效能。通过自动化工具和方法,DevOps团队可以实现以下目标:消除手动和重复性任务。简化流程。在整个软件开发生命周期中实现更
  • 三分钟白话RocketMQ系列—— 如何发送消息

    我们知道RocketMQ主要分为消息 生产、存储(消息堆积)、消费 三大块领域。那接下来,我们白话一下,RocketMQ是如何发送消息的,揭秘消息生产全过程。注意,如果白话中不小心提到相关代
  • 为什么你不应该使用Div作为可点击元素

    按钮是为任何网络应用程序提供交互性的最常见方式。但我们经常倾向于使用其他HTML元素,如 div span 等作为 clickable 元素。但通过这样做,我们错过了许多内置浏览器的功能。
  • 华为Mate 60保护壳曝光:硕大后置相机模组 凸起程度有惊喜

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • 机构称Q2国内智能手机销量同比下滑4% vivo份额重回第1

    7月29日消息,根据市场调查机构Counterpoint Research公布的最新报告,2023年第2季度中国智能手机销量同比下降4%,创新自2014年以来第2季度销量新低。报
  • 联想的ThinkBook Plus下一版曝光,键盘旁边塞个平板

    ThinkBook Plus 是联想的一个特殊笔记本类别,它在封面放入了一块墨水屏,也给人留下了较为深刻的印象。据有人爆料,联想的下一款 ThinkBook Plus 可能更特殊,它
Top