在golang中是否有习惯用法的作用域语义?

11
我想知道是否有一种习惯用语来表示“作用域”语义。所谓作用域,指的是以下情况:
  • 作用域互斥锁(一行代码代替显式的锁定和延迟解锁),
  • 记录函数(或任何代码块)的进入和退出,
  • 测量执行时间。
第一和第二个例子的示例代码:
package main

import "log"
import "sync"

func Scoped(m *sync.Mutex) func() {
    m.Lock()
    return func() {
        m.Unlock()
    }
}

func Log(what string) func() {
    log.Println(what, "started")
    return func() {
        log.Println(what, "done")
    }
}

func main() {
    defer Log("testing")()

    m := &sync.Mutex{} // obviously mutex should be from other source in real life
    defer Scoped(m)()
    // use m
}

https://play.golang.org/p/33j-GrBWSq

基本上,我们需要进行一次函数调用(例如互斥锁),并且其中一次调用应该被延迟执行(例如互斥解锁)。我建议在这里只返回一个未命名的函数,但也可以轻松地给它命名(返回一个带有函数字段的结构体)。
只有一个问题:用户可能会忘记“调用”第一次调用的结果。
这段代码(可以)算是惯用写法吗?

我想不出更短的方法了。 "惯用" 的方式是显式调用 lock() 然后 defer unlock() - Not_a_Golfer
这种方法在Effective Go跟踪示例中被使用。 - Charlie Tumahai
4个回答

8
将匿名函数视为“作用域”:
func() {
    Entrance()
    defer Exit()
    // anything you want to do in this scope
}()

1
无法相信这不是最佳答案。 - Jim Wood
它只是将被接受答案的内容放入匿名函数中,因此在任何方面都不是新颖的。 - Kokos

5
您的解决方案已经很好了。您返回了一个func类型的值,这也必须在defer的末尾调用。
您可以避免返回func值,但必须有两个函数调用,一个记录开始事件,另一个记录结束事件。
另一种选择是进行函数调用,产生延迟函数的参数值(而不是返回函数),该值将使用defer语句进行评估,这样它仍然可以保持一行。
您也可以在Go Playground上尝试它。
func start(s string) string {
    fmt.Println("Started", s)
    return s
}

func end(name string) {
    fmt.Println("Ended", name)
}

func main() {
    defer end(start("main"))

    fmt.Println("Doing main's work...")
}

输出:

Started main
Doing main's work...
Ended main

回顾了半个十年之后,它看起来比被接受的答案略微好一些,主要是因为它使用了更简单的概念。我只会改变 start() 的返回类型/end() 的参数为某个自定义类型,以强制用户不会忘记调用 start()(虽然仍然可能省略 end(),但这种情况可能性较小)。 - Kokos

4

我不相信有一种惯用的方法可以做到这一点。我也不确定为什么你想要这样做,难道写下面的代码会造成很大困扰吗?

m.Lock()
defer m.Unlock()

?


3
这确实是惯用的方式(天啊,我开始讨厌这些惯用语了),但我认为OP提出的模式相当好。 - Not_a_Golfer
当您只需要输入一行时,“日志引擎”是最好的选择 - 如果有更多行,则用户放弃在某些小函数中记录的可能性会增加。 - Kokos
在我们的示例记录器中,还有可能使用错误的变量(i 而非 _j_)或输入不同的字符串(这可能会导致日志分析工具混淆)。 - Kokos

0

我认为这个问题与Go的习惯用法并不相关,似乎在函数行为一致时,更好地对代码进行推理。为了保持状态,最好创建一个对象并将函数定义为该对象的方法。大概意思是:

type message string
func (foo message) Log(bar string){
    if bar==nil{doSomethingSpecial()}
    switch foo{
        case something: doSomething()
        ...
        case nil: doSomethingInitial()
        default: doDefault()
    }   
    log.Println(bar, "started")
    foo=bar
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接