前言
今天开始,会经常写一些小的代码片段,放在这个“远离枯燥的golang”系列里面。众所周知,golang是一门语法非常简单的语言,其实每天用一门这样的语言写代码,功能实现很快,但是可能会渐渐失去写代码的乐趣,感觉每天都在写if err != nil
。曾经就有个笑话说,连java程序员都写完代码了,golang程序员还在写if err != nil
。所以就会把工作中突发奇想想到的一些我自认为有点意思的golang代码的小片段记录在这里。
base64解码
一般来说,用到base64最频繁的场景就是,有一个base64编码过的string
,然后将其解码成另一个string
,用最简单的方式操作大概就是这样:
package main
import "base64"
import "fmt"
func main(){
b64Str := "dGhpcyBpcyBhIGV4YW1wbGU="
var res string
if bytes ,err := base64.StdEncoding.DecodeString(b64Str);err != nil {
fmt.Println("err!")
} else {
res = string(res)
}
}
这种平平无奇的代码写多就会想吐呢!今天我想了下,至少写一点审美情趣不一样的代码吧。我首先就是不想看到这个多值返回!而且他喵的返回值是个字节数组,真是讨厌
回调替代多值返回
于是,我先写出了如下代码:
package main
import "base64"
import "fmt"
func myb64Decode(f func([]bytes),b64Str string) error {
if bytes ,err := base64.StdEncoding.DecodeString(b64Str) ;err != nil {
return error
} else {
f(bytes)
}
}
func main(){
b64Str := "dGhpcyBpcyBhIGV4YW1wbGU="
var res string
if err := myb64Decode(func (bytes []byte){
res = (string)bytes
},b64Str) ;err != nil {
fmt.Println("err!")
}
}
看上去其实并没有太好。(但是用上了闭包就看起来厉害一点了啊!)至少不用处理我个人非常不爱的多值返回了。
改进那个闭包
再想想,其实这个回调本质只是想要一个从[]byte
到string
的setter函数。不如写成固定的实现好了。
package main
import "base64"
import "fmt"
func myb64Decode(f func([]bytes),b64Str string) error {
if bytes ,err := base64.StdEncoding.DecodeString(b64Str) ;err != nil {
return error
} else {
f(bytes)
}
}
func setter(str *string) func([]byte) {
return func(bytes []byte) {
*str = string(bytes)
}
}
func main(){
b64Str := "dGhpcyBpcyBhIGV4YW1wbGU="
var res string
if err := myb64Decode(setter(&res),b64Str);err != nil {
fmt.Println("err!")
}
}
业务代码好像变得看起来好一点了!当然,其实setter是可以隐藏在函数内部直接复制的,让他看上去行为就是从一个base64的string
,解码并复制到了另一个string
的引用中去。但是我懒得打字了。
最终我的写法
其实写成上一节的话呢,已经可以达到目的了。但是我今天写的功能里面有一个特殊的地方,解码完成以后,是替换掉原先的变量的值的,也就是说,如果我写到上一节为止,我的代码会是这样的
myb64Decode(setter(&b64Str),b64Str)
作为一个重度懒癌,我拒绝同一个变量写两次!于是我又套了一层,写了一个不通用的实现。
package main
import "base64"
import "fmt"
func myb64Decode(f func([]bytes),b64Str string) error {
if bytes ,err := base64.StdEncoding.DecodeString(b64Str) ;err != nil {
return error
} else {
f(bytes)
}
}
func setter(str *string) func([]byte) {
return func(bytes []byte) {
*str = string(bytes)
}
}
func b64ToSelf(str * string) error {
return myb64Decode(setter(str),*str)
}
func main(){
b64Str := "dGhpcyBpcyBhIGV4YW1wbGU="
if err := b64ToSelf(&b64Str);err != nil {
fmt.Println("err!")
}
}
结语
最终大概就是这样!最近新写的系统是rest api,api里解出来的参数基本都是base64编码过的,所以会频繁地操作解码,这样写大概会舒服一点!
本文的代码都是按照记忆重新写的,均没有经过测试!
结语的结语:为什么我不喜欢多值返回
首先,多值返回让原本很好用的golang类型推导运算符:=
变得非常痛苦。
当我们写出
if x, err := function() ; err != nil {
}
就意味着,这里的x
是一个作用域非常狭窄的变量,大多数时候我们都不希望函数返回值太过短暂;如果不是真的需要这样,可能需要另一个赋值语句把结果保存到生命周期更长的变量里,简直就是脱裤子放屁!
那么,我们就得写出
x, err := function()
if err != nil {
}
或者
var x interface{}
var err error
if x, err = function() ; err != nil {
}
这两种写法都恶心的地方在于,为了x ,我必须拥有一个生命周期一样长的error!所以多值返回的问题在于,多值返回的变量会享有同样的生命周期,我控制不了!虽然看上去没什么大不了,但是学c++出身的人!受!不!了!(教练!我想写rust!)
所以上文所说的,通过引用赋值规避多值返回,能控制住error
类型变量的周期在很短的语句里!也不需要看到结果变量被赋值来赋值去了!