惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
Cloudbric
Cloudbric
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
宝玉的分享
宝玉的分享
I
Intezer
Simon Willison's Weblog
Simon Willison's Weblog
博客园_首页
The Cloudflare Blog
C
Cisco Blogs
AWS News Blog
AWS News Blog
IT之家
IT之家
Cyberwarzone
Cyberwarzone
罗磊的独立博客
美团技术团队
V
V2EX
Project Zero
Project Zero
A
Arctic Wolf
C
Cyber Attacks, Cyber Crime and Cyber Security
大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 聂微东
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
雷峰网
雷峰网
S
Schneier on Security
P
Privacy International News Feed
V
Visual Studio Blog
量子位
T
Tor Project blog
S
Securelist
腾讯CDC
A
About on SuperTechFans
T
Threat Research - Cisco Blogs
G
GRAHAM CLULEY
B
Blog RSS Feed
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
B
Blog
NISL@THU
NISL@THU
L
Lohrmann on Cybersecurity
V
Vulnerabilities – Threatpost
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
L
LINUX DO - 热门话题
Recorded Future
Recorded Future

smallyu的博客

为什么买的加密货币一定要去中心化 RISC-V 虚拟机无法代替 EVM 虚拟机 Polkadot 的 Existential Deposit 机制错在哪里 利息,套利,交易策略,金融市场 LayerZero 从来不挑战比特币的地位 默认参数引起的以太坊节点运行错误 硬件钱包与资产安全 预测市场比币圈更赌场 为什么去中心化的跨链桥不可能实现 我对于 AI 时代的答案 不要投资任何隐私币 区块链技术世界的三大真理 以太坊 AA 钱包的致命问题是什么 基于 AI 语义执行的 MCP 区块链的设计 一个集成 Geth 和 CometBFT 的兼容层 我的加密货币定投策略(二) DeFi 基础: 理解 AMM 定价机制 Go 语言 GMP 调度器的原理是什么 Web3 项目分析计划 对 Psyche Network 项目的分析 continuation 教程: 理解 CPS 如何开发一个比特币符文(Runes)协议 比特币脚本开发教程 我从王垠的计算机科学视频班学到了什么 区块链技术面试题(2025年版) Rust 语言容易让新手困惑的一个“过度优化” Solana 智能合约开发教程 (1) 尝试开发一个最小 EVM 虚拟机 基于 ZK 的链上身份系统设计 一个 Web3 打赏系统的设计 鼓吹 Cursor 的人技术能力都差 关于 Code Review 的礼节 假如启动一个新的以太坊 PoS 网络 发行加密货币的最好方式 所有 BFT 共识的区块链都是中心化的 对 2025 年区块链行业的预测 Restaking 项目的经济难题 如何看懂任意区块链项目的技术架构 为什么不要做以太坊的二次开发 为什么不要做智能合约开发和 DeFi 开发 我的加密货币定投策略(一) PoS 类型的区块链如何处理分叉 Ethereum Casper 为什么需要 EIP-7251 区块链中的 PBFT 不需要第二次投票 开发者的思维方式 发币的核心要点 炒币投资的小 tips 为什么说 PoW 比 PoS 更加去中心化 牛市对普通人来说意味着什么 如何健康地远程工作 为什么比特币不用概率加密函数 程序员的 “服从权威” 心理 区块链技术面试题 如何区分公有链和联盟链 对 Layer 2 项目创业想法的回复 对区块链共识机制的理解 Pebbling Game 鹅卵石游戏 PDP 文件证明的局限性 不要小瞧 ChatGPT 为什么炒币不是一个好主意 一种在区块链上生成随机数的机制 为什么以太坊的私钥计算不可逆 关于以太坊的私钥碰撞 “猜均值的2/3” dApp 游戏设计 Proof of Storage/Space/Replication 的区别 Proofs of Retrievability 文件证明的含义 对 S-PDP 文件证明的示例和解释 我的加密货币交易机器人 对区块链行业的见闻 随机确认块的共识机制 VRF + BFT 共识引起交易失败的问题 为什么要重视编程思想 对 Web 3.0 的理解 GitBook 好用吗? 一种区块链节点存储扩容的方式 一种基于“自我中心主义”的共识机制 链表常见算法题及解析 基于 Multi-Linked List 的区块链设想 理解哈希函数与序列化 联盟链比公有链差在哪儿 在 Dijkstra 算法中保存路径 为什么数字货币使用区块链是政治问题 区块链:下一代数字身份认证体系的基石 网页技术能实现 3D 建模吗? 给区块链一个定义 从 Erlang 开始了解 Actor 模型 一种侧边导航栏的交互方式 Rust 的 ownership 是什么? Haskell 中的 Monad 是什么? 浅析 Libra 背后的区块链技术 对区块链的理性认识 Rust 基础语法概述 基于 Java 的爬虫框架 WebCollector Kotlin:简化版的 Scala JavaScript 有关联数组吗? 主流编程语言的异常处理机制 Scala 语法基础 用 Scala 改写 Java 浅度实践 Python 获取海贼王更新信息 HTML5 音乐可视化
Go 语言基本语法
2019-03-15 · via smallyu的博客

2019-03-15

Go语言虽然在语言设计上不被王垠看好,但它如此简洁的代码结构确实让人着迷。

语句

Go语言语句结尾不需要;

变量和常量

使用var声明变量。当变量需要初始化时,可以使用赋值符号:=代替=以省略var关键字。

var a int
var b string

var c int = 10
var d = "golang"  // 编译器自动推断类型
d := 10

与C语言或Java不同,Go语言的类型声明在变量右侧。需要注意的是,如果程序中声明的变量未经使用,程序将无法通过编译。Go语言是一种工程化的语言,因此它的一些特性让人感觉不可理喻,但又会在实际工程中提高效益。

Go语言的变量赋值支持一些炫酷的写法,比如要交换变量x和y的值,可以使用这种违反直觉的写法:

x, y = y, x

Go语言中使用const定义常量,truefalseiota是预定义常量。其中iota稍显特殊,iota会在每一个const关键字出现时重置为0,然后在下一次const出现前,每出现一次iota,iota的值加1。

const a = iota   // 0
const b = iota   // 0
const (
  c = iota       // 0
  d = iota       // 1
)

数组和切片

声明一个元素个数为3的数组,并初始化:

array := [3]int{0, 1, 2}
array[0] = 3
fmt.Println(array)

和其他语言一样,Go语言在声明数组后并不能改变数组的大小。所以Go语言提供了像Python一样的切片。切片可以从数组中产生,也可以使用make()函数新建。

array := [3]int{0, 1, 2}
slice1 := array[:2]       // 从数组中创建

slice2 := make([]int, 3)  // 直接创建

fmt.Println(slice1)       // [0 1]
fmt.Println(slice2)       // [0 0 0]

除切片外,映射也是使用make函数创建,映射的类型全称是var myMap map[string] int,意为声明变量myMap,key为string,value为int。

流程控制

Go语言允许if-else语句的条件表达式不加小括号,当然加上也无妨。

a := 1
if a == 1 {
  print(1)
} else if (a == 2) {
  print(2)
} else {
  print(3)
}

选择语句的条件表达式同样不需要小括号,另外也不需要break,其他匹配项并不会执行,这一点和Scala相同。对选择语句的优化貌似已经是不约而同的做法。

i := 0
switch i {
case 0:
  print(0)
case 1:
  print(1)
}

循环结构的条件表达式依然不需要小括号。Go语言只支持for循环。同时对无限循环的场景也做了优化,不再需要for(;;)的写法。

for {
  print(1)
}

函数

Go语言诞生自C语言的派系,因此Go语言从一开始就不是OOP或FP的语言,没有类、对象等概念。函数是程序中的一等公民。和C语言相同,(main包下的)main函数是整个程序的入口。

func add(a int, b int) (int, int) {
  return a + b, a - b
}

func main() {
  x, y := add(1, 2)
  print(x, y)
}

Go语言的语句简洁高效,函数名后的第一个括号为入参,第二个括号是出参。函数支持多返回值。如果参数类型相同,可以将类型声明合并到一起,如(a, b int)

结构体

刚才提到Go语言没有类、对象等概念,但是Go语言有类似C语言的结构体,并且能力强大。这里定义一个Person结构体,包含两个属性name和age,并为Person添加一个方法getInfo,用于输出Person对象的信息:

type Person struct {
  name string
  age int
}

func (p Person) getInfo() {
  print(p.name, p.age)
}

func main() {
  smallyu := new(Person)
  smallyu.name = "smallyu"
  smallyu.age = 1
  smallyu.getInfo()
}

用OOP的思想理解这样的程序并不违和。除了结构体,Go语言还保留有指针的概念。Java程序员对指针可能稍感陌生,关于指针在结构体方法中的应用,可以通过一个简单的例子来了解:

type Person struct {
  name string
}

func (p Person) setName() {
  p.name = "set name"
}

func (p *Person) setName2() {
  p.name = "set name"
}

func main() {
  smallyu := &Person{"smallyu"}
  smallyu.setName()
  fmt.Println(smallyu)        // &{smallyu}

  bigyu := &Person{"bigyu"}
  bigyu.setName2()
  fmt.Println(bigyu)          // &{set name}
}

使用值类型定义的结构体方法,入参为形参;使用引用类型定义的结构体方法,入参为实参。&{}是初始化对象的方法之一,等同于new()

匿名结合

Go语言中匿名结合的概念,相当于OOP语言的继承。一个结构体可以继承另一个结构体的属性和方法,大致是这样。

type Father struct {
  name string
}

func (f Father) getName() {
  print(f.name)
}

type Son struct {
  Father
}

func main() {
  smallyu := &Son{}
  smallyu.name = "smallyu"
  smallyu.getName()       // smallyu
}

Son并没有定义name属性,也没有定义getName()方法,它们均继承自Father。

接口

Go语言的接口是非侵入式的,结构体只要实现了接口中的所有方法,程序就会认为结构体实现了该接口。

type IPerson interface {
  getName()
}

type Person struct {
  name string
}

func (p Person) getName() {
  print(p.name)
}

func main() {
  var smallyu IPerson = &Person{"smallyu"}
  smallyu.getName()
}

协程

使用协程的关键字是go,从命名就能看出协程对于Go语言的重要性、协程是轻量级的线程,启动一个协程非常简单:

func f(msg string) {
  println(msg)
}

func main() {
  f("直接调用方法")
  go f("协程调用方法")
}

运行程序,你会发现程序只打印出”直接调用方法”几个字。这种情况是不是似曾相识?go启用了另一个”线程”来打印消息,而main线程早已结束。在程序末尾加上fmt.Scanln()阻止main线程的结束,就能看到全部的打印内容。

通道

通道即协程之间相互通信的通道。

func main() {
  message := make(chan string)

  go func() {
    message <- "ping"
  }()

  msg := <-message
  println(msg)
}

make函数返回一个chan string类型的通道,在匿名函数中将字符串”ping”传入通道,之后将通道中的数据输出到变量msg,最后打印出msg的值为”ping”。

错误处理

Go语言在错误处理部分有两个函数较为常用,panic函数和defer函数。panic函数会打印错误消息,并终止整个程序的执行,类似Java的Throw Exception;defer函数会在当前上下文环境执行结束前再执行,类似try catch后的finally;panic函数虽然会终止整个程序,但不会终止defer函数的执行,可以将defer函数用于打印日志。这是一个简单的例子:

func main() {
  println("beginning")
  defer func() {
    println("defer")
  } ()
  println("middle")
  panic("panic")
  println("ending")
}

来分析一下程序的执行结果。首先beginning被打印;然后遇到defer,暂不打印;middle在defer之前被打印;遇到panic,程序将终止,打印defer和panic。

这里要注意,defer是在程序结束前执行,而不是在其他语句结束后执行,这是有区别的。就像这里,panic函数引起了当前程序的结束,所以defer会在panic函数前执行,而不是panic后。程序的执行结果如下:

beginning
middle
defer
panic: panic

goroutine 1 [running]:
main.main()
    D:/go/src/awesomeProject/main.go:12 +0x7f

其他

除此之外Go语言还有很多语言特性,也提供了非常多实用的工具包。Go语言是一种值得我们尝试去使用的语言。关于协程和通道,后续会单独探讨这一重要特性。

参考