英文:
Is unnamed arguments a thing in Go?
问题
我正在为Go编写一个Go解析器,并为了测试它,我从GitHub项目中下载了一堆文件。
在https://github.com/andlabs/ui中,我遇到了一个包含以下代码片段的文件:
func moveLabel(*Button) {
from := movingCurrent
to := 0
if from == 0 {
to = 1
}
movingBoxes[from].Delete(0)
movingBoxes[to].Append(movingLabel, false)
movingCurrent = to
}
看到一个没有名称的Button
指针作为函数参数有点让我困惑,这使得在函数内部无法引用它。
然而,鉴于编译器没有报错,它似乎在语法上是正确的。
在Go中,无名称的函数参数有什么用途?
英文:
I am writing a parser in Go for Go, and to test it I downloaded a bunch of files from github projects.
In https://github.com/andlabs/ui I bumped into a file containing this piece of code:
func moveLabel(*Button) {
from := movingCurrent
to := 0
if from == 0 {
to = 1
}
movingBoxes[from].Delete(0)
movingBoxes[to].Append(movingLabel, false)
movingCurrent = to
}
It confuses me a bit to see a pointer to a Button
without a name as a function argument, which makes it impossible to reference from inside the function.
However, it seems to be syntactically correct given that the compiler doesn't complains.
What is the purpose of unamed functions arguments in Go?
答案1
得分: 44
未命名的参数是完全有效的。规范中的参数声明如下所示:
ParameterDecl = [ IdentifierList ] [ "..." ] Type .
可以看到,IdentifierList
(标识符名称)用方括号括起来,表示它是可选的。只有Type
是必需的。
之所以这样设计,是因为对于调用方法或函数的人来说,名称并不重要。重要的是参数的类型和顺序。这在这个回答中有详细说明:https://stackoverflow.com/questions/31377433/getting-method-parameter-names-in-golang/31377793#31377793
通常,你给变量和参数命名是为了能够引用它们。
当你不给某个东西命名时,是因为你不想引用它。
所以问题应该是:为什么我不想引用一个参数?
例如,因为参数“存在”(被传递了),但你不需要它,不想使用它。如果我不需要它,为什么它会存在呢?
因为某人或某些东西“规定”特定的参数必须存在。例如,你想要实现一个接口,或者你想要传递一个函数值,其签名由预期的函数类型定义。
让我们看一个例子。我们有以下MyWriter
接口:
type MyWriter interface {
Write(p []byte) error
}
这是一个简化的io.Writer
,它只返回一个错误,而不报告写入的字节数。如果你想提供一个只丢弃数据的实现(类似于ioutil.Discard
),那么实现不使用(不需要使用)它的参数:
type DiscardWriter struct{}
func (DiscardWriter) Write([]byte) error { return nil }
就是这样:我们不使用接收器,也不使用参数。两者都可以没有名称。实现正好做到了应该做的事情。
这样做(使用未命名的参数)还可以“记录”该值未被使用/引用。
另一个原因可能是为了提供“向前”兼容性。如果你发布一个库,你不能改变或扩展参数列表,否则会破坏向后兼容性(而在Go中没有函数重载:如果你想要具有不同参数的2个变体,它们的名称也必须不同)。因此,你可以提前声明一个带有额外参数的导出函数或方法,但由于你尚未使用它们,你可以将它们保持未命名。这个答案中详细说明了这个例子:https://stackoverflow.com/questions/22549228/why-does-go-allow-compilation-of-unused-function-parameters/33759531#33759531
这里需要注意的一点是,你不能混合使用命名和未命名的参数。如果你给一些参数命名了,那么所有参数都必须命名。如果你不需要所有参数,你可以像这个例子中一样使用空白标识符:
一个简单的 Web 服务器,对所有请求返回"Hello"
文本:
http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
io.WriteString(w, "Hello")
})
panic(http.ListenAndServe(":8080", nil))
处理函数只使用响应写入器w
,而不使用请求结构,因此使用空白标识符作为其名称。
另一个相关的问题:
还有一个相关的问题,关于使用/命名返回值:
以及关于获取方法/函数参数名称的问题:
英文:
Unnamed parameters are perfectly valid. The Parameter declaration from the spec:
ParameterDecl = [ IdentifierList ] [ "..." ] Type .
As you can see, the IdentifierList
(the identifier name or names) is in square brackets, which means it's optional. Only the Type
is required.
The reason for this is because the names are not really important for someone calling a method or a function. What matters is the types of the parameters and their order. This is detailed in this answer: https://stackoverflow.com/questions/31377433/getting-method-parameter-names-in-golang/31377793#31377793
Generally you name variables and parameters so that you can refer to them.
When you don't name something, it's because you don't want to refer to it.
So the question should rather be: Why would I not want to refer to a parameter?
For example because the parameter "is there" (it is passed), but you don't need it, you don't want to use it. Why would it be there if I don't need it?
Because someone or something dictates for specific parameters to be there. For example you want to implement an interface, or you want to pass a function value whose signature is defined by the function type that is expected.
Let's see an example. We have the following MyWriter
interface:
type MyWriter interface {
Write(p []byte) error
}
A simplified io.Writer
which only returns an error, but does not report the number of bytes written. If you'd want to provide an implementation which just discards the data (similar to ioutil.Discard
), then the implementation does not use (does not need to use) its argument:
type DiscardWriter struct{}
func (DiscardWriter) Write([]byte) error { return nil }
And that's all: we don't use the receiver, we don't use the argument. Both can be unnamed. And the implementation does exactly what it should.
Doing so (using unnamed parameters) also documents that the value is not used / referred to.
Another reason can be to provide forward compatibility. If you release a library, you can't change or extend the parameter list without breaking backward compatibility (and in Go there is no function overloading: if you want 2 variants with different parameters, their names must be different too). So you may declare an exported function or method with additional parameters early, but since you don't use them yet, you may leave them unnamed. An example of this is detailed in this answer: https://stackoverflow.com/questions/22549228/why-does-go-allow-compilation-of-unused-function-parameters/33759531#33759531
One thing to note here is that you can't mix named and unnamed parameters. If you name some, you must name all. If you don't need all, you may use the blank identifier like in this example:
A simple web server which responds with the "Hello"
text to all requests:
http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
io.WriteString(w, "Hello")
})
panic(http.ListenAndServe(":8080", nil))
The handler function sending back the "Hello"
text only uses the response writer w
, but not the request structure, so the blank identifier is used as its name.
Another related question:
Also somewhat related, but regarding using / naming returned values:
And regarding getting method / function parameter names:
答案2
得分: 7
未命名的参数是有效的,但不能被引用。它们只是为了满足接口和签名的要求。
英文:
Unnamed parameters are valid but not referenceable.
They're just for satisfaction of interfaces and signatures.
答案3
得分: 3
未命名函数参数的目的是用于函数的参数(函数的局部变量),这些参数在函数的代码中没有被引用,因此不需要名称。有趣的是,匿名变量实际上比你想象的更常用。在Go语言中,函数的返回值通常被列为类型,但实际上它们也是函数的局部变量,可以被命名和操作。
在golang.org的"Effective Go"页面上可以看到以下示例:
func ReadFull(r Reader, buf []byte) (n int, err error) {
for len(buf) > 0 && err == nil {
var nr int
nr, err = r.Read(buf)
n += nr
buf = buf[nr:]
}
return
}
英文:
The purpose of unnamed function arguments is for arguments (which are local variables of the function) which are not referred to in the function's code, and therefore do not need a name. An interesting note about anonymous variables is that they are actually used more commonly than you may think. In Go, a function's return value(s) are usually listed as types, but actually they are also local variables of the function which can be named and manipulated.
See this example in the "Effective Go" page on golang.org
https://golang.org/doc/effective_go.html#named-results
func ReadFull(r Reader, buf []byte) (n int, err error) {
for len(buf) > 0 && err == nil {
var nr int
nr, err = r.Read(buf)
n += nr
buf = buf[nr:]
}
return
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论