可变参数函数中是否需要格式化指令?

问题描述 投票:0回答:2

我有一个可变参数函数

respond
,它接受多个位置参数,并在末尾扩展一个
any
切片。根据上下文,该函数以不同的方式使用最后一个参数(请注意
message
data
如何根据
status
的值而有所不同。):

func respond(w http.ResponseWriter, status int, format string, a ...any) {
    var (
        statusOk bool
        message  string
        data     interface{}
    )
    if status >= 300 {
        statusOk = false
        message = fmt.Sprintf(format, a...)
        data = nil
        log.Error().Msg(message)
    } else {
        statusOk = true
        message = format
        data = a[0]
        log.Info().Msg(fmt.Sprintf("%d", status))
    }
    // issue response
    responder := render.New()
    responder.JSON(w, status, map[string]interface{}{
        "statusOk": statusOk,
        "status":   status,
        "message":  message,
        "data":     data,
    })
}

对于下面的调用,我没有收到任何警告:

respond(w, http.StatusNotFound, "person %v does not exist", personId)

但是,对于下一个,会引发警告

respond call has arguments but no formatting directives
(但代码按预期运行,
person
struct
):

respond(w, http.StatusAccepted, "updated", person)

以我的业余眼光来看,看起来可变参数函数期望在末尾有一个格式化字符串和该格式的参数。但为什么会这样呢?可变参数函数的作用是否有限制?

或者我应该更好地将

responder
分成两部分,每种情况一个(“好”和“不好”)?


另请参阅 https://github.com/golang/go/issues/26486https://go.dev/src/cmd/vet/testdata/print/print.go(第 316 行)了解在 Go 代码中对该消息的讨论(我得到的是来自 linter 的警告,正如 @mkopriva 和 @JimB 在他们的评论中提到的那样)

go formatting variadic-functions
2个回答
3
投票

警告来自 linter,因为您调用

respond()
函数时使用的格式字符串与之后提供的参数不匹配。

此特定消息来自 printf 分析器,它记录了:

printf:检查 Printf 格式字符串和参数的一致性

该检查适用于格式化函数(例如 fmt.Printf 和 fmt.Sprintf)的调用,以及任何检测到的这些函数的包装器

由于您的

response()
函数使用
format
a
参数按原样调用
fmt.Sprintf()
,因此这被视为
fmt.Sprintf()
的包装器。

如果您想避免/摆脱此警告消息,最简单的“修复”是不按原样使用这些参数,例如复制其中一个参数,然后传递:

// Make and use a copy to avoid being marked as a wrapper of fmt.Sprintf
format := format
message = fmt.Sprintf(format, a...)

0
投票

错误是因为您的

format
参数正在
Sprintf
中使用,而在第二种情况下,
updated
字符串没有任何格式指令,即:
%v

respond(w, http.StatusAccepted, "updated", person)

你可能应该重构你的函数。

考虑这种方法:

注意:您不需要设置

statusOk = false
data = nil
,它们是该类型的默认值。


func respond(w http.ResponseWriter, status int, message string, a ...any) {
    var (
        statusOk bool
        message  string
        data     interface{}
    )

    if status >= 300 {
        log.Error().Msg(message)
    } else {
        statusOk = true
        data = a[0]
        log.Info().Msg(fmt.Sprintf("%d", status))
    }

    // issue response
    responder := render.New()
    responder.JSON(w, status, map[string]interface{}{
        "statusOk": statusOk,
        "status":   status,
        "message":  message,
        "data":     data,
    })
}

func example() {

    message := fmt.Sprintf("person %v does not exist", personId)
    respond(w, http.StatusNotFound, message, personId)

    message = "updated"
    respond(w, http.StatusAccepted, message, person)
}
© www.soinside.com 2019 - 2024. All rights reserved.