golang 中是否有惯用的作用域语义?
Is there idiomatic scoped semantics in golang?
我想知道是否有任何惯用的方式来表示 scoped 语义。通过范围我的意思是这样的:
- 作用域互斥(oneliner 而不是显式锁定 + 延迟解锁),
- 记录函数(或任何代码块)的入口和出口,
- 测量执行时间。
前两个项目符号的示例代码:
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
基本上我们需要刚才调用一个函数(例如mutex lock),还有一个调用应该推迟到defer(例如mutex unlock)。我建议在这里 returning 未命名函数,但它可以很容易地命名(return 带有函数字段的结构)。
只有一个问题:用户可能会忘记 'call' 第一次调用的结果。
这段代码是(可以)惯用的吗?
我认为没有惯用的方法可以做到这一点。我不确定你为什么要写,真的这么糟糕吗
m.Lock()
defer m.Unlock()
?
您提出的解决方案已经很不错了。您 return 一个 func
类型的值,您还必须在 defer
的末尾调用它。
您可以避免这种情况(returning 一个 func
值),但是必须有 2 个函数调用,一个记录 start 事件另一个记录 end 事件。
另一种方法是进行一个函数调用,该函数调用产生延迟函数的参数值(而不是 returning 一个函数),该参数值使用 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
我认为问题与 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
}
将匿名函数作为范围:
func() {
Entrance()
defer Exit()
// anything you want to do in this scope
}()
我想知道是否有任何惯用的方式来表示 scoped 语义。通过范围我的意思是这样的:
- 作用域互斥(oneliner 而不是显式锁定 + 延迟解锁),
- 记录函数(或任何代码块)的入口和出口,
- 测量执行时间。
前两个项目符号的示例代码:
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
基本上我们需要刚才调用一个函数(例如mutex lock),还有一个调用应该推迟到defer(例如mutex unlock)。我建议在这里 returning 未命名函数,但它可以很容易地命名(return 带有函数字段的结构)。
只有一个问题:用户可能会忘记 'call' 第一次调用的结果。
这段代码是(可以)惯用的吗?
我认为没有惯用的方法可以做到这一点。我不确定你为什么要写,真的这么糟糕吗
m.Lock()
defer m.Unlock()
?
您提出的解决方案已经很不错了。您 return 一个 func
类型的值,您还必须在 defer
的末尾调用它。
您可以避免这种情况(returning 一个 func
值),但是必须有 2 个函数调用,一个记录 start 事件另一个记录 end 事件。
另一种方法是进行一个函数调用,该函数调用产生延迟函数的参数值(而不是 returning 一个函数),该参数值使用 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
我认为问题与 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
}
将匿名函数作为范围:
func() {
Entrance()
defer Exit()
// anything you want to do in this scope
}()