大家好,我是煎鱼。 在各种写业务代码的时候,大家会常常要处理字符串的内容。常见的像是用邮箱登陆账号,如果是:eddycjy@gmail.com,那就得根据 @ 来切割,分别取出前和后,来识别用户名和邮箱地址。 这种需求,在 Go 里写起来方便吗?今天就由煎鱼带大家了解。 背景 重复代码 无独有偶,Ainar Garipov 在许多项目中遇到了前面我们所提的切割需求。 例如: idx = strings.Index(username, "@") if idx != -1 { name = username[:idx] } else { name = username } 又或是:
大家好,我是煎鱼。 我有一个朋友,,开开心心入职,想着施展拳脚,第一个任务就是对老旧的二进制文件进行研究。 他一看,这文件,不知道是编译器用什么参数怎么打出来的,环境不知道是什么,更不知道来自什么代码分支? 这除了是项目流程上的问题外,Go 在这块也有类似的小问题,处理起来比较麻烦。 背景 日常中很难从 Go 二进制文件中检索元信息,要么是信息完全缺失,要么提取需要对二进制文件进行大量解析。 包含的元信息如下: 元信息 提取处 Go 构建版本 符号表,通过全局变量 runtime.buildVersion 来获取 构建信息,例如:模块和版本 符号表,通过全局变量 runtime/debug.modinfo 来获取 编译器选项,例如:构建模式、编译器、gcflags、ldflags 等 无法获取 用户定义的自定义数据,例如:应用程序版本等 需在编译时设置全局字符串变量,才可以获取 关注到编译器选项,也就是参数等都是无法得知的,也就是会提高获取如何编译出来的难度。
大家好,我是煎鱼。 Go 的依赖管理,也就是 Go Module。从推出到现在,也已经有了一定的年头了,吐槽一直很多,官方也不断地在进行完善。 Go1.18 将会推出一个新特性:Multi-Module Workspaces,用于支持 Module 多工作区,能解决以往的一系列问题。 今天将由煎鱼带大家一起深入学习。 背景 在日常使用 Go 工程时,总会遇到 2 个经典问题,特别的折腾人。 如下: 依赖本地 replace module。 依赖本地未发布的 module。 replace module 第一个场景:像是平时在 Go 工程中,我们为了解决一些本地依赖,或是定制化代码。会在 go.
大家好,我是煎鱼。 有一个读者刚入门 Go ,提了一个很有意思的问题:Go 有几种种声明变量的方式,作为初学者,到底用哪种,有什么区别,又为什么要有多种声明方式呢? 为此,煎鱼将和大家一起探索这个问题。 变量声明 在 Go 中,一共有 2 种变量声明的方式,各有不同的使用场景。 分别是: 标准变量声明(Variable declarations)。 简短变量声明(Short variable declarations) 标准声明 变量声明创建了一个或多个变量,为它们绑定了相应的标识符,并给每个变量一个类型和初始值。 使用语法: VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
大家好,我是煎鱼。 很多小伙伴学习 Go 语言的语法时,可能只是轻轻地看到过这个问题,结果一旦上手,多多少少一个组内总会碰到过几次。 甚至会发现有一定年限的程序员也会遇到。有小伙伴疑惑了,这么折腾,为什么 Go 不直接在语言层面就支持 map 并发,那得有多香? 为什么原生不支持 凭什么 Go 官方还不支持,难不成太复杂了,性能太差了,到底是为什么? 官方答复原因如下(via @go faq): 典型使用场景:map 的典型使用场景是不需要从多个 goroutine 中进行安全访问。 非典型场景(需要原子操作):map 可能是一些更大的数据结构或已经同步的计算的一部分。 性能场景考虑:若是只是为少数程序增加安全性,导致 map 所有的操作都要处理 mutex,将会降低大多数程序的性能。 核心来讲就是:Go 团队在经过了长时间的讨论后,认为原生 map 更应适配典型使用场景。
大家好,我是煎鱼。 Go1.18 的泛型是闹得沸沸扬扬,虽然之前写过很多篇针对泛型的一些设计和思考。但因为泛型的提案之前一直还没定型,所以就没有写完整介绍。 如今已经基本成型,就由煎鱼带大家一起摸透 Go 泛型。本文内容主要涉及泛型的 3 大概念,非常值得大家深入了解。 如下: 类型参数。 类型约束。 类型推导。 类型参数 类型参数,这个名词。不熟悉的小伙伴咋一看就懵逼了。 泛型代码是使用抽象的数据类型编写的,我们将其称之为类型参数。当程序运行通用代码时,类型参数就会被类型参数所取代。也就是类型参数是泛型的抽象数据类型。 简单的泛型例子: func Print(s []T) { for _, v := range s { fmt.Println(v) } } 代码有一个 Print 函数,它打印出一个片断的每个元素,其中片断的元素类型,这里称为 T,是未知的。
大家好,我是煎鱼。 最近在我们 Go 的技术交流群里,有一个小伙伴提了一个程序方面的问题,还挺有意思的,分享给大家。 示例 示例程序如下: type T struct{} func (t *T) Hello() string { if t == nil { fmt.Println("脑子进煎鱼了") return "" } return "煎鱼进脑子了" } func main() { var t *T t.
大家好,我是煎鱼。 有一次事故现场,在紧急恢复后,他正在排查代码,查了好一会。我回头一看,这错误提醒很明显就是致命错误,较好定位。 但此时,他竟然在查 panic-recover 是不是哪里漏了,我表示大受震惊… 今天就由煎鱼给大家分享一下错误类型有哪几种,又在什么场景下会触发。 错误类型 error 第一种是 Go 中最标准的 error 错误,其真身是一个 interface{}。 如下: type error interface { Error() string } 在日常工程中,我们只需要创建任意结构体,实现了 Error 方法,就可以认为是 error 错误类型。 如下: type errorString struct { s string } func (e *errorString) Error() string { return e.
大家好,我是煎鱼。 前两天 Go1.18 beta1 已经发布,距离正式发布 Go1.18 的生产可用还有 2 个月,也就是泛型即将正式面世。 最近正在收集泛型的一些资料,看到在 2015 年有人在 Hacker News 上的《Go 1.5 max procs default》吐槽 Go 不支持泛型是 “政治” 原因… 看了还是有些意义的,与现在的矛盾点基本一致,为此分享给大家。 网友吐槽 网友 @aikah 认为 Go 团队不太可能在语言中加入泛型,这显然是一个政治问题而不是技术问题。错误处理也是如此。
大家好,我是煎鱼。 程序里的锁,是很多小伙伴在写分布式应用时用的最多的一个利器之一。 使用 Go 的同学里,绝大部分都有其他语言的经验,就会对其中一点有疑惑,那就是 Go 里的锁,竟然不支持可重入? 为此,今天煎鱼带大家一起来了解这里的设计考量,看看为什么。 可重入锁 如果对已经上锁的普通互斥锁进行 “加锁” 操作,其结果要么失败,要么会阻塞至解锁。 锁的场景如下: 在加锁上:如果是可重入互斥锁,当前尝试加锁的线程如果就是持有该锁的线程时,加锁操作就会成功。 在解锁上:可重入互斥锁一般都会记录被加锁的次数,只有执行相同次数的解锁操作才会真正解锁。 简单来讲,可重入互斥锁是互斥锁的一种,同一线程对其多次加锁不会产生死锁,又或是导致阻塞。 不同语言间实现可能或多或少有些区别,但大体意思差不多。 请你想一下,Go 是怎么样的呢? Go 支持情况 我们看到以下这个 Go 互斥锁例子: var mu sync.Mutex func main() { mu.