初学者GO 之旅 (1) 包、变量、函数
本篇文章为go的启蒙文章,非常适合新手阅读,本文文笔生动形象,配图有趣可爱,实为下饭上厕所无聊打发时间的良心文字,欢迎大家阅读+评论。本文大部分文档来源于go官网教程:https://tour.golang.org/list 。也有一些第三方资料。
Packages (包)
所有的go程序都是由包组成的,意思就是说你不能像拍簧片(php) 一样, 这里拉一坨,那里拉一坨,你必须像java一样,所有的代码都对应着一个包,在包里面编写代码。懂了吗? 还不懂?给我爬。。。。。。
但是呢,就像你出生在普通家庭,隔壁王二蛋却是个富二代一样。虽然大家都是包,但是有个包就比较特殊,那就 main 包(正好我认识一个昵称叫main的人)。所有的程序入口都是由main包开始的,就像你写c程序一样,必须要 void main() 。
import
go程序都是由 import 关键字来导入包。import 一般在程序中都是导入的意思,类似的还有其他语言里面的 include(包含)require (依赖)等。每个语言都有自己的关键字。这里我们只说go。
一般来说 按照约定,程序包名称与导入路径的最后一个元素相同。例如,“ math/rand” 就有rand这个包的文件。当然,go也很贴心的为你准备了两种import写法。
第一种 写个() 把所有的代码包起来 (go官方推荐)
import ( "fmt" "math/rand" )
第二种 用双引号,一行一行的写
import "fmt" import "math/rand"
好了 下面看一段比较完整代码:
package main import ( "fmt" "math/rand" ) func main() { fmt.Println("My favorite number is", rand.Intn(10)) }
下面我们分析下这段代码:
第一行,声明是main包
第二行,使用import关键字。引入了2个包。fmt是输出包,类似php里面的 echo
第六行,我们定义了一个main 方法 mian包里只能有一个唯一的main方法,
第七行,我们用fmt包里面的 Println 输出你最喜欢的数字。
来看看,这个rand.Intn方法,哦 我的上帝,这是个伪随机数方法,所以我们这么写只会返回一个固定的值。
这真是个糟糕的决定,它看起来就像保罗叔叔的臭靴子一样恶心!
所以,应该怎么正确输出随机数呢。不对!这个问题我们不应该放在这里讲,后面我们会讲到,我发誓!
被导出的变量
在go里面,如果一个包里面的某个变量是大写字母开头,那么它就是public权限的,就是可以被导出。当你使用 import 一个包的时候,
默认你就拥有了这个包所有大写字母开头的变量和方法的权限。当然了 小写字母开头的变量和方法是无法使用的。来看看代码:
package main import ( "fmt" "math" ) func main() { fmt.Println(math.Pi) //这个是可以的 fmt.Println(math.pi) //这个就报错了 }
Variables (变量)
go的变量声明和其他语言不太一样,go是类型在后面的声明,下面看代码:
var a int var c,d,e bool
变量定义既可以在包里,也可以在函数里,这两种变量的上下文会有所不同。变量也可以在定义的时候初始化:
var gwyy int = 3
在函数内部,可以使用短语句来定义变量,不需要写完整的变量类型,这种定义还是比较方便的,大部分函数内我们都喜欢这么用。不过要注意哦,在函数外部是不可以的。
func gwyy() { a := 3 b := true c := "fff" }
基本类型
bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // alias for uint8 rune // alias for int32 float32 float64 complex64 complex128
基本类型大概有这么几个。int,uint和uintptr类型在32位系统上通常为32位宽,在64位系统上为64位宽。当您需要整数值时,应该使用int,除非有特殊原因要使用大小或无符号整数类型。
go有个体贴的地方,如果你声明变量没有赋值的话,go会默认给你初始化0值。
数字类型 默认 0
布尔类型默认 false
字符串类型 默认 “”
func main() { var i int var f float64 var b bool var s string fmt.Printf("%v %v %v %q\n", i, f, b, s) } //0 0 false ""
类型转换
表达式T(v)将值v转换为类型T。看个例子:
func main() { //一些数字转换 var i int = 42 var f float64 = float64(i) var u uint = uint(f) //或者更简单的 i := 42 f := float64(i) u := uint(f) }
类型推断
在声明变量而不指定显式类型时(通过使用:=语法或var =表达式语法),将从右侧的值推断出变量的类型。
func main() { var i int j := i // 自动推断是 int //下面这些,右侧包含无类型的数字常量时,新变量可以是int,float64或complex128,具体取决于常量的精度: i := 42 // int f := 3.142 // float64 g := 0.867 + 0.5i // complex128 }
常量
常量像变量一样声明,但是带有const关键字。常量可以是字符,字符串,布尔值或数字值。不能使用:=语法声明常量。
const Pi = 3.14
Functions
go里面也是有函数的,一个函数可以接受0个或者多个参数。看下面的例子,add函数就接受了2个参数。这里有个小注意点,参数类型是放在变量名后面的。
package main import "fmt" func add(x int, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) }
这里有个语法糖,当函数多个参数类型一样的时候,可以只写一次类型像下面这样:
package main import "fmt" func add(x,y int) int { return x + y } func main() { fmt.Println(add(3,4)) }
下面我们再看下函数的多返回值,这个特性是我几乎在别的语言里面没看到过,go的一个函数可以允许你定义多个返回值,这个东西得好好唠唠,这个东西比想象中要复杂。先看个例子:
func swap(x, y string) (string, string) { return y, x }
这个方法返回了2个返回值 ,其实就是一个简单的交换方法,如果java来做,可能就只能返回个map, 我们可以看到,在返回值的位置,我们是写了2个返回值类型,代表要返回几个返回值,类型是什么。
命名返回值
看下面的例子,你又发现了什么呢:
func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return }
是不是很神奇,定义了2个返回值,但是return啥也没返回,就写了个空return。这个叫什么呢,这个叫命名返回值,就是你在函数声明的时候,就先把返回值给定义好,这个时候,最骚的操作来了,你根本不需要return任何狗屁东西直接就写个 return 就好了。是不是很神奇,大家可以再多看看。而且你再这个函数内 x y的变量也不用定义,因为在 “命名返回值” 里你自己已经定义好了。这种不带参数的return语句返回命名的返回值就是所谓的“裸”回报。
其实这里go会把这2个变量当做你再函数顶部定义过了。但是!,这里有个大坑大家得注意下。看下代码:
func gwyy() (item []string) { item[0] = "aaa" return }
看起来是不是好像没问题,但是会直接panic:runtime error: index out of range [0] with length 0 。这是为什么呢。其实换个写法大家应该就能看懂了。
func gwyy() { var item []string item[0] = "aaa" return }
其实刚才的命名返回值,我们拆开了就是这样的,可以看到 item变量只是声明了,根本没初始化。所以会直接报错。需要再代码最顶端加个初始化就好了
func gwyy() { var item []string item = make([]string,3,3) item[0] = "aaa" return }
所以说这里要特别注意下,如果使用了命名返回值,基本类型不需要初始化了,go会默认给你初始化成0值或者“”,引用类型,像是 map,slices,channels,struct 等需要初始化的。当然啦 最后记得不要忘记写下 return。这里也有个建议。如果裸返回值不要再很长的函数内使用。否则会降低函数的可读性。