Skip to content

Commit

Permalink
docs: update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
suyuan32 committed May 21, 2024
1 parent 954fc71 commit 3c1d8fb
Show file tree
Hide file tree
Showing 2 changed files with 308 additions and 0 deletions.
154 changes: 154 additions & 0 deletions src/en/guide/concepts/golang/50-closure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
order: 50
title: Closure
icon: line-md:sun-rising-twotone-loop
head:
- - meta
- name: keywords
content: Go, Golang, closure, function factory, iterator
---

## What Is a Closure?

::: info What Is a Closure?
A closure is an entity composed of a function and the associated referencing environment. In simple terms, a closure is a function that references variables outside its own scope. The lifetime of this function can extend beyond the scope in which it was created.

Example

```go
package main

import "fmt"

func main() {
count := func() func() int {
i := 0 // Initialize a local variable within the function
return func() int {
i++ // Increment the local variable
return i
}
}()

fmt.Println(count())
fmt.Println(count())
}

// Output
// 1
// 2
```

Notice that `i` is a local variable within the `count` function. Executing the function twice might lead one to expect the output to be `1` both times. However, the actual output is `1` followed by `2`. The reason is that when assigning the closure to a variable (`count`), it retains a pointer to `i`. As a result, `i` is preserved beyond the function's execution due to escape analysis. **If the function is not assigned to a variable, executing it multiple times will yield consistent results.**
:::

## Use Cases

### Middleware

When defining web middleware, we often encounter code like the following:

```go
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return
}
fn(w, r, m[2]) // If no issues, continue executing fn
}
}
```

In this example, we return an `http.HandlerFunc` that calls the `fn` function. This allows us to achieve chainable operations—executing middleware code while still continuing with the main function.

## State Sharing

Closures can be used to share state across multiple invocations of a function. A common example is an iterator:

```go
package main

import "fmt"

func main() {
num := []int{1, 2, 3, 4}

iterator := func(arr []int) func([]int) (int, bool) {
i := -1
return func(arr []int) (int, bool) {
i++
if i < len(arr) {
return arr[i], true
}
return 0, false
}
}

iter := iterator(num)

for {
value, ok := iter(num)
if !ok {
return
}

fmt.Println(value)
}
}

// Output
// 1
// 2
// 3
// 4
```

## Callback Functions

We can also pass callback functions as parameters:

```go
func GetData(data int, callback func(int)) {
go func() {
result := data + 2
callback(result)
}()
}
```

In the above example, after passing in `data`, the `callback` can access `result` for additional callback operations.

## Function Factories

Closures allow us to create function factories by returning functions based on input parameters:

```go
func CalculationFactory(operation string) func(int, int) int {
switch operation {
case "add":
return func(a, b int) int {
return a + b
}
case "subtract":
return func(a, b int) int {
return a - b
}
case "multiply":
return func(a, b int) int {
return a * b
}
case "divide":
return func(a, b int) int {
if b != 0 {
return a / b
}
return 0
}
default:
return nil
}
}
```

By passing in `"add"`, you can obtain an addition function, and by passing in `"divide"`, you can obtain a division function.
154 changes: 154 additions & 0 deletions src/guide/concepts/golang/50-closure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
order: 50
title: 闭包
icon: line-md:sun-rising-twotone-loop
head:
- - meta
- name: keywords
content: Go, Golang, closure, 闭包, 函数工厂, 迭代器, 中间件, 回调函数
---

## 什么是闭包?

::: info 什么是闭包?
闭包是由函数和与其相关的引用环境组合而成的实体。简单来说,闭包就是一个引用了作用域之外的变量的函数(Func),该函数的存在时间可以超过创建他的作用域。

例子

```go
package main

import "fmt"

func main() {
count := func() func() int {
i := 0 // 初始化函数内变量
return func() int {
i ++ // 函数内变量加 1
return i
}
}()

fmt.Println(count())
fmt.Println(count())
}

// 结果
// 1
// 2
```

我们会注意到 `i``count` 的局部变量,执行两次函数感觉上应该是都输出 `1`, 实际上输出的是 `1, 2`, 原因是在赋值时 `count` 保留着对 `i` 的指针,因此 `i` 在逃逸分析后被保留,没有随着函数的执行完毕而结束。**如果函数没有赋值给变量,则执行多次结果会保持不变。**
:::

## 使用场景

### 中间件

我们在定义 web 中间件时经常会看到以下形式的代码:

```go
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return
}
fn(w, r, m[2]) // 如果没问题则继续执行 fn
}
}
```

可以看到, 我们返回了一个 `http.HandlerFunc`, 这个函数里面调用了 fn, 这样的话我们就可以实现链式操作,既执行了中间件代码,又可以继续执行函数,非常方便。

## 状态共享

闭包可以用来共享多次执行函数的状态, 常见的例子是迭代器:

```go
package main

import "fmt"

func main() {
num := []int{1, 2, 3, 4}

iterator := func(arr []int) func([]int) (int, bool) {
i := -1
return func(arr []int) (int, bool) {
i ++
if i < len(arr) {
return arr[i], true
}
return 0, false
}
}

iter := iterator(num)

for {
value, ok := iter(num)
if !ok {
return
}

fmt.Println(value)
}
}

// 结果
//1
//2
//3
//4
```

## 回调函数

我们也可以通过传参,实现传入回调函数

```go
func GetData(data int, callback func(int)) {
go func() {
result := data + 2
callback(result)
}
}
```

上面的例子可以看到, 我们传入 `data` 后, `callback` 可以获取到 `result` 进行额外回调操作。

## 函数工厂

通过闭包我们还可以构造函数工厂,通过传入参数返回对应函数。

```go
func CalculationFactory(operation string) func(int, int) int {
switch operation {
case "add":
return func(a, b int) int {
return a + b
}
case "subtract":
return func(a, b int) int {
return a - b
}
case "multiply":
return func(a, b int) int {
return a * b
}
case "divide":
return func(a, b int) int {
if b != 0 {
return a / b
}
return 0
}
default:
return nil
}
}
```

我们可以传入 `add` 获取加法函数,`divide` 获取除法函数。

0 comments on commit 3c1d8fb

Please sign in to comment.