19

One of the more notable aspects of Go when coming from C is that the compiler will not build your program if there is an unused variable declared inside of it. So why, then, is this program building if there is an unused parameter declared in a function?

func main() {
    print(computron(3, -3));
}


func computron(param_a int, param_b int) int {
    return 3 * param_a;
}
icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    There are a couple of linters that will do it: https://github.com/mvdan/unparam, http://github.com/nakabonne/unusedparam – Bryan Dec 09 '21 at 09:30

2 Answers2

30

There's no official reason, but the reason given on golang-nuts is:

Unused variables are always a programming error, whereas it is common to write a function that doesn't use all of its arguments.

One could leave those arguments unnamed (using _), but then that might confuse with functions like

func foo(_ string, _ int) // what's this supposed to do?

The names, even if they're unused, provide important documentation.

Andrew

https://groups.google.com/forum/#!topic/golang-nuts/q09H61oxwWw

Sometimes having unused parameters is important for satisfying interfaces, one example might be a function that operates on a weighted graph. If you want to implement a graph with a uniform cost across all edges, it's useless to consider the nodes:

func (graph *MyGraph) Distance(node1,node2 Node) int {
    return 1
}

As that thread notes, there is a valid argument to only allow parameters named as _ if they're unused (e.g. Distance(_,_ Node)), but at this point it's too late due to the Go 1 future-compatibility guarantee. As also mentioned, a possible objection to that anyway is that parameters, even if unused, can implicitly provide documentation.

In short: there's no concrete, specific answer, other than that they simply made an ultimately arbitrary (but still educated) determination that unused parameters are more important and useful than unused local variables and imports. If there was once a strong design reason, it's not documented anywhere.

Linear
  • 21,074
  • 4
  • 59
  • 70
  • 6
    I agree. Sometimes you will need to implement a function with unused parameters in order to satisfy an interface. – Herman Junge Mar 21 '14 at 04:35
  • 2
    I get it - first class functions and functions as types. Cool! –  Mar 21 '14 at 09:05
  • 1
    This seems like it could be readily covered by an idiom similar to `_ = node1 _=node2` – jrodman Feb 12 '15 at 18:10
  • 1
    The idiom suggested by @jrodman (separate lines like `_ = node1`; `_ = node2`) seems better to me, because once learned, it explicitly disambiguates "this is intentionally unused" from "this is accidentally unused". The latter can happen during clumsy maintenance, and it's nice to have an explicit programmatic idiom for people to explicitly affirm "I actually did intentionally mean for this to be unused", and even nicer if the accidental implicit case is simply prevented. – mtraceur Nov 03 '20 at 22:14
4

The main reason is to be able to implement interfaces that dictate specific methods with specific parameters, even if you don't use all of them in your implementation. This is detailed in @Jsor's answer.

Another good reason is that unused (local) variables are often the result of a bug or the use of a language feature (e.g. use of short variable declaration := in a block, unintentionally shadowing an "outer" variable) while unused function parameters never (or very rarely) are the result of a bug.

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).

You may provide an exported function or method and add extra - not yet used - or optional parameters (e.g. hints) to it in the spirit that you may use them in a future version / release of your library.

Doing so early will give you the benefit that others using your library won't have to change anything in their code.

Let's see an example:

You want to create a formatting function:

// FormatSize formats the specified size (bytes) to a string.
func FormatSize(size int) string {
    return fmt.Sprintf("%d bytes", size)
}

You may as well add an extra parameter right away:

// FormatSize formats the specified size (bytes) to a string.
// flags can be used to alter the output format. Not yet used.
func FormatSize(size int, flags int) string {
    return fmt.Sprintf("%d bytes", size)
}

Then later you may improve your library and your FormatSize() function to support the following formatting flags:

const (
    FlagAutoUnit      = 1 << iota // Automatically format as KB, MB, GB etc.
    FlagSI                        // Use SI conversion (1000 instead of 1024)
    FlagGroupDecimals             // Format number using decimal grouping
)

// FormatSize formats the specified size (bytes) to a string.
// flags can be used to alter the output format.
func FormatSize(size int, flags int) string {
    var s string

    // Check flags and format accordingly
    // ...

    return s
}
icza
  • 389,944
  • 63
  • 907
  • 827