首页 > 其他分享 >Golang Block 到底是什么? `i:=i` 合法? 为什么能解决闭包变量冲突?

Golang Block 到底是什么? `i:=i` 合法? 为什么能解决闭包变量冲突?

时间:2023-03-14 11:35:23浏览次数:43  
标签:闭包 变量 req Golang switch Block go 隐式 block

Golang Block 到底是什么? i:=i 合法? 为什么能解决闭包变量冲突?

什么? 你告诉我 i:=i 不仅合法,而且还常用。甚至能解决并发编程中的变量冲突?

以下这段代码出自 golang 官方Effective GO 并发编程章节。 为了解决 goroute 中变量 req 冲突, 使用了语句 req := req

https://golang.org/doc/effective_go#concurrency

我们来看看代码

func Serve(queue chan *Request) {
    for req := range queue {
        req := req // Create new instance of req for the goroutine.
        sem <- 1
        go func() {
            process(req)
            <-sem
        }()
    }
}

req := req 这种写法是不是感到很奇怪? 看看 Effective GO 怎么说?

but it's legal and idiomatic in Go to do this. You get a fresh version of the variable with the same name, deliberately shadowing the loop variable locally but unique to each goroutine.

不仅合法,而且还常用。 这么做是为了在循环体内部将得到一个同名变量, 以隐藏 循环变量 req, 从而每个 goroute 得到一个唯一 req

直接这么看,还是有点拗口。 不过不重要, 在了解了 golang 的 区块(block) 定义范围之后, 就迎刃而解了。

Blocks

https://golang.org/ref/spec#Blocks

A block is a possibly empty sequence of declarations and statements within matching brace brackets.

什么是 Blocks

  1. 大括号 {} 包围的一个代码块。
  2. 这个代码块内容也可以为空, 也可以是有内容。
Block = "{" StatementList "}" .
StatementList = { Statement ";" } .

Block 的范围在哪里?

除了我们上面说的 以大括号{}包围的代码块 这种 显式 block 之外, go 语言还存在几种 隐式 的 block。

1. universe 全局

The universe block encompasses all Go source text.

universe 这个词不怎听说, 但是换成 全局 这个概念还是很好理解的。

2. package

Each package has a package block containing all Go source text for that package.

package 就是最常见的 package 包。 作用域也很明确, 使用类似 package.Variable

3. file 文件

Each file has a file block containing all Go source text in that file.

文件级别的隐式 block。 这个其实还是有点意思的。

目前发现现象,

  1. test文件 filename_test.go 中的 变量/函数 , 在 主程序文件 filename.go 中是无法引用的。
  2. 主程序文件 中的 ` 变量/函数test文件 中是无法引用的。
  3. test文件 之间的是可以互相引用的。
  4. 主程序 之间的是可以互相引用的。

因此推测(无实锤), 1. 存在 file block。 2. 并且有高低等级之分。

其实很好理解, _test 是用于测试的, 肯定不能干扰主干程序的的环境。

注意: 图片中是两个文件, 上 main_test.gomain.go。 并且 编译器 很明显的提示了, 在 main.go 中找不到变量 VarInTest

4. for, if, switch 的隐式 block

Each "if", "for", and "switch" statement is considered to be in its own implicit block.

  1. for, if, switch 本身是一个 隐式的 block
  2. 其语法中的 大括号{} 所包围的区域是一个 显式的 Block

  1. for block (19-26 行) 本身就是一个 隐式的 block
  2. for 大括号{} 部分(20-25行) 的 是一个 显式的 block , 作为 for block子 block (statement block) 存在

因此, 在 22 行 i:=i 是合法的, **在 statement block 中产生了 同名变量覆盖**。

for-block-2.png

也就是因为 {} 是 for 子block 的原因, for 的 post 可以修改变量 i, 在 statement 中也可以修改变量 i

  1. 因此, 在 35 行被注释的时候, for block 的变量 i 被继承,并在 if block 中被修改, 所以结果是 loop: 0,1,2,9
  2. 当 35 行存在的时候, for block 中的变量 istatement block 继承, 并进行 同名覆盖 , 之后以 _i 说明。 所以, 在 if block 继承了 statement block 中的 _i 并修改。 此时, for blocki 并未受到影响。 因此结果是: loop: 0-9

5. switch / selectclause 的 隐式 block

Each clause in a "switch" or "select" statement acts as an implicit block.

  1. switch / select 更为特殊一点, 除了包含 大括号 以外, 还包含条件语句逻辑。 并且 条件语法代码块 也是一个 隐式的 block
  2. 这个 隐式 block 包含了 case / default: 本身之外, 还包含了 下一层的缩进 Statement 区域

  1. 注释 20 行, 可以很清楚的看到报错, func block 中的 i 在申明后并未使用。 此说明了 switch 本身是一个 隐式 block。
  2. switch clause 分支 整体 (case 10-14 行)/(default 15-17) 是一个 block。

互相吹捧, 共同进步

欢迎和我一起学习进步, 如果有什么问题, 可以给我私信留言。 或者

公众号

标签:闭包,变量,req,Golang,switch,Block,go,隐式,block
From: https://blog.51cto.com/bashrc/6120131

相关文章

  • JavaScript作用域闭包(你不知道的JavaScript)
    JavaScript闭包,是JS开发工程师必须深入了解的知识。3月份自己曾撰写博客《​​JavaScript闭包​​》,博客中只是简单阐述了闭包的工作过程和列举了几个示例,并没有去刨根问底,......
  • (转)golang goquery selector(选择器) 示例大全
    原文:https://juejin.cn/post/6844903552867893255最近研究Go爬虫相关的知识,使用到goquery这个库比较多,尤其是对爬取到的HTML进行选择和查找匹配的内容时,goquery的选择器......
  • golang示例项目 客户信息关系系统
    1.需求分析1)模拟实现基于文本界面的《客户信息管理软件》2)该软件能够实现对客户对象的插入、修改和删除(用切片实现),并能够打印客户明细表2.项目界面设计1)主菜单页面---......
  • 【转】Golang Reflect反射的使用详解1 --- makeFunc的使用
     转,原文: https://studygolang.com/articles/12300 ---------------------------------- Whatyouarewastingtodayistomorrowforthosewhodiedyesterday......
  • 04 Golang 运算符
    一、算术运算符运算符描述+相加-相减*相乘/相除%求余代码示例:1packagemain2​3import"fmt"4​5funcmain(){6//......
  • Golang的defer用法
    直观理解deferpackagemainimport("fmt")funcmain(){fmt.Println("deferbegin")//将defer放入延迟调用栈deferfmt.Println(1)def......
  • Golang使用命令行改变PATH路径
    goenv-wENV_VAR=value这是内置在goCLI中的跨平台解决方案,将来应该可以为您节省一些时间。例:goenv-wGOPATH=/your/desired/path输入goenv以检查当前环境......
  • Golang项目使用Dockerfile部署
    前言关于在构建golang编写的web项目中使用dockerfile的一些总结。通过查阅资料后,写下了如下配置:一般模式会安装golang编译环境,镜像文件包会比较大。#构建golang运行......
  • (转)golang 读写文件的四种方式
    原文:https://blog.csdn.net/whatday/article/details/103938124读文件读取的文件放在file/test:也就是file包下的test这个文件,里面写多一点文件读文件方式一:利用ioutil.R......
  • swift逃逸闭包和自动闭包
    当闭包作为一个实际参数传递给一个函数的时候,并且它会在函数返回之后调用我们就说这个闭包逃逸了,当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写@escaping......