在Go语言中,是否有习惯用法的作用域语义?

huangapple go评论74阅读模式
英文:

Is there idiomatic scoped semantics in golang?

问题

我想知道是否有一种习惯用法来表示"scoped"语义。所谓的"scoped"是指以下情况:

  • 作用域互斥锁(一行代码代替显式的Lock + 延迟的Unlock),
  • 记录函数(或任何代码块)的进入和退出,
  • 测量执行时间。

第一和第二点的示例代码如下:

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

基本上,我们需要进行一次"刚刚"的函数调用(例如互斥锁的锁定),而另一次调用应该延迟到defer(例如互斥锁的解锁)。我建议在这里只返回一个无名函数,但也可以轻松地命名它(返回一个带有函数字段的结构体)。

只有一个问题:用户可能会忘记"调用"第一次调用的结果。

这段代码(可以)是惯用的吗?

英文:

I wonder if there is any idiomatic way to represent scoped semantics. By scoped I mean things like:

  • scoped mutex (oneliner instead of explicit Lock + deffered Unlock),
  • logging function (or any code block) entrance and exit,
  • measuring execution time.

Example code for first two bullets:

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

Basically we need to make one function call just now (eg mutex lock), and one call should be postponed to defer (eg mutex unlock). I propose just returning unnamed function here, but it can be easily named (return struct with function field).

There is only one problem: user can forget to 'call' result of first call.

This code is (can be) idiomatic?

答案1

得分: 7

将匿名函数作为作用域

func() {
    Entrance()
    defer Exit()
    // 在这个作用域中可以执行任何你想做的操作
}()

请注意,以上是代码示例,不需要进行翻译。

英文:

Take anonymous function as a scope:

func() {
    Entrance()
    defer Exit()
    // anything you want to do in this scope
}()

答案2

得分: 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
英文:

Your proposed solution is already nice. You return a value of func type which you also have to call at the end of the defer.

You can avoid that (returning a func value), but there have to be 2 function calls, one that logs the start event and another one that logs the end event.

The alternative is to make a function call which produces the parameter value of the function that is deferred (rather than returning a function) which is evaluated with the defer statement, and this way it still can remain one line.

You can also try it on the 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...")
}

Output:

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

答案3

得分: 4

我不相信有一种惯用的方法来做这件事。我也不确定为什么你想这样做,难道使用以下方式真的那么糟糕吗?

m.Lock()
defer m.Unlock()
英文:

I do not believe there is an idiomatic way to do this. I'm not sure why you'd want to either, is it really so bad to write

m.Lock()
defer m.Unlock()

?

答案4

得分: 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
}
英文:

I think question isn't relevant to Go idiomaticity, Seems it's generally better to reason about code when function behave identically either call. To keep state I'd better make an object and define function as method on that object. Means something like

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
} 

huangapple
  • 本文由 发表于 2015年1月27日 20:11:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/28170234.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定