Go 中显式作用域块的用途?

Purpose of an explicitly scoped block in Go?

我正在阅读 MicroMDM SCEP 存储库中的源代码,https://github.com/micromdm/scep/blob/1e0c4b782f3f2e1e6f81da5f82444a6cedc89df3/cmd/scepclient/scepclient.go#L54-L65:

func run(cfg runCfg) error {
    ctx := context.Background()
    var logger log.Logger
    {
        if strings.ToLower(cfg.logfmt) == "json" {
            logger = log.NewJSONLogger(os.Stderr)
        } else {
            logger = log.NewLogfmtLogger(os.Stderr)
        }
        stdlog.SetOutput(log.NewStdlibAdapter(logger))
        logger = log.With(logger, "ts", log.DefaultTimestampUTC)
        if !cfg.debug {
            logger = level.NewFilter(logger, level.AllowInfo())
        }
    }
    lginfo := level.Info(logger)

我想知道显式块(外部{ ... })的目的是什么?这段代码会不会和去掉后完全一样,比如

func run(cfg runCfg) error {
    ctx := context.Background()
    var logger log.Logger
    if strings.ToLower(cfg.logfmt) == "json" {
        logger = log.NewJSONLogger(os.Stderr)
    } else {
        logger = log.NewLogfmtLogger(os.Stderr)
    }
    stdlog.SetOutput(log.NewStdlibAdapter(logger))
    logger = log.With(logger, "ts", log.DefaultTimestampUTC)
    if !cfg.debug {
        logger = level.NewFilter(logger, level.AllowInfo())
    }
    lginfo := level.Info(logger)

也许显式块只是为了提高易读性?

在这种情况下,额外的块似乎没有任何意义。块内没有声明变量。它不会增加清晰度,反而会让您感到困惑。

如果需要清晰,您可以将该代码提取到一个新函数中以初始化记录器。

func initLogger(cfg runCfg) log.Logger {
    var logger log.Logger

    if strings.ToLower(cfg.logfmt) == "json" {
        logger = log.NewJSONLogger(os.Stderr)
    } else {
        logger = log.NewLogfmtLogger(os.Stderr)
    }
    stdlog.SetOutput(log.NewStdlibAdapter(logger))
    logger = log.With(logger, "ts", log.DefaultTimestampUTC)
    if !cfg.debug {
        logger = level.NewFilter(logger, level.AllowInfo())
    }

    return logger
}

func run(cfg runCfg) error {
    ctx := context.Background()
    logger := initLogger(cfg)
    lginfo := level.Info(logger)
    ...

我最好的猜测是这个块在过去有过某种用途,但更改代码的人并没有删除它,也可能不确定是否仍然有用。翻看这个函数的blame log,或许会给你答案。

花点时间阅读其余代码,这些块是 all over the code。但在这些情况下,有 个声明的短期变量,这是一种在不再需要它们后丢弃它们的方法。两种最可能的情况是记录器可能有一些,或者它只是开发人员选择的特定样式。实际上,两者都可能是正确的。

对于 Go 来说,这有点奇怪,但可以有效地明确告诉垃圾收集器和开发人员这些已经超出范围。然而,Go 编译器和 GC 已经足够先进,知道什么时候可以丢弃它们,所以除了整理当前范围的命名空间之外,对程序本身没有什么好处。

虽然我同意@Schwern 的观点,但它增加了清晰度并实现了相同的结果,将它们重构为它们自己的函数。如果有人花时间明确声明作用域块,那么为什么不利用这段时间让它们发挥作用呢?

链接代码块:

var svc scepserver.Service // scep service
{
    svcOptions := []scepserver.ServiceOption{
        scepserver.ChallengePassword(*flChallengePassword),
        scepserver.WithCSRVerifier(csrVerifier),
        scepserver.CAKeyPassword([]byte(*flCAPass)),
        scepserver.ClientValidity(clientValidity),
        scepserver.AllowRenewal(allowRenewal),
        scepserver.WithLogger(logger),
    }
    svc, err = scepserver.NewService(depot, svcOptions...)
    if err != nil {
        lginfo.Log("err", err)
        os.Exit(1)
    }
    svc = scepserver.NewLoggingService(log.With(lginfo, "component", "scep_service"), svc)
}

var h http.Handler // http handler
{
    e := scepserver.MakeServerEndpoints(svc)
    e.GetEndpoint = scepserver.EndpointLoggingMiddleware(lginfo)(e.GetEndpoint)
    e.PostEndpoint = scepserver.EndpointLoggingMiddleware(lginfo)(e.PostEndpoint)
    h = scepserver.MakeHTTPHandler(e, svc, log.With(lginfo, "component", "http"))
}