Есть ли идиоматическая семантика в golang?
Интересно, есть ли какой-то идиоматический способ представления семантики в области видимости? Под областью я имею в виду такие вещи, как:
- ограниченный мьютекс (oneliner вместо явного Lock + deffered 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
По сути, нам нужно сделать один вызов функции только сейчас (например, блокировка мьютекса), и один вызов следует отложить до отсрочки (например, разблокировка мьютекса). Я предлагаю просто вернуть безымянную функцию здесь, но она может быть легко названа (вернуть struct с полем функции).
Есть только одна проблема: пользователь может забыть "вызвать" результат первого вызова.
Этот код (может быть) идиоматический?
4 ответа
Я не верю, что есть идиоматический способ сделать это. Я не уверен, почему вы хотите, это действительно так плохо, чтобы написать
m.Lock()
defer m.Unlock()
?
Возьмите анонимную функцию в качестве области видимости:
func() {
Entrance()
defer Exit()
// anything you want to do in this scope
}()
Ваше предлагаемое решение уже приятно. Вы возвращаете значение func
тип, который вы также должны позвонить в конце defer
,
Вы можете избежать этого (возвращая func
значение), но должно быть 2 вызова функций, один из которых регистрирует событие начала, а другой регистрирует событие конца.
Альтернативой является создание вызова функции, который выдает значение параметра отложенной функции (а не возвращает функцию), которое оценивается с помощью defer
утверждение, и таким образом он все еще может оставаться одной строкой.
Вы также можете попробовать это на игровой площадке Go:
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
}