Есть ли идиоматическая семантика в 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
} 
Другие вопросы по тегам