Can Go have optional parameters? Or can I just define two different functions with the same name and a different number of arguments?
Go does not have optional parameters nor does it support method overloading:
Method dispatch is simplified if it doesn't need to do type matching as well. Experience with other languages told us that having a variety of methods with the same name but different signatures was occasionally useful but that it could also be confusing and fragile in practice. Matching only by name and requiring consistency in the types was a major simplifying decision in Go's type system.
make
a special case, then? Or is it not even really implemented as a function… –
Aldred make
is a language construct and the rules mentioned above don't apply. See this related question. –
Dichlorodifluoromethane range
is the same case as make
, in that sense –
Wengert interface{}
as a type is not a long way advanced from passing void*
in C and is nearly as bad an idea. –
Dumpling ...interface{}
as simpler than strongly-typed optional arguments. It's simpler for the compiler implementer, sure, but for people using the language, it leads to an ugly mess of length checks and type assertions at the top of the function. AFAICT making life simpler for the compiler implementer at the expense of ugly, error-prone boilerplate for the user is never a language feature, always a defect. –
Dumpling nil
-checks and the frequent need of reflections for information which you would expect to be available out of the box, especially if you've got to work with interface{}
or pointers. I mean, just check how you're supposed to read data from a sql.Row
... and that's just the tip of the iceberg. –
Reliant A nice way to achieve something like optional parameters is to use variadic args. The function actually receives a slice of whatever type you specify.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
is a slice of ints –
Basrhin You can use a struct which includes the parameters:
type Params struct {
a, b, c int
}
func doIt(p Params) int {
return p.a + p.b + p.c
}
// you can call it without specifying all parameters
doIt(Params{a: 1, c: 9})
The main advantage over an ellipsis (params ...SomeType
) is that you can use the param struct with different parameter types.
For arbitrary, potentially large number of optional parameters, a nice idiom is to use Functional options.
For your type Foobar
, first write only one constructor:
func NewFoobar(options ...func(*Foobar) error) (*Foobar, error){
fb := &Foobar{}
// ... (write initializations with default values)...
for _, op := range options{
err := op(fb)
if err != nil {
return nil, err
}
}
return fb, nil
}
where each option is a function which mutates the Foobar. Then provide convenient ways for your user to use or create standard options, for example :
func OptionReadonlyFlag(fb *Foobar) error {
fb.mutable = false
return nil
}
func OptionTemperature(t Celsius) func(*Foobar) error {
return func(fb *Foobar) error {
fb.temperature = t
return nil
}
}
For conciseness, you may give a name to the type of the options (Playground) :
type OptionFoobar func(*Foobar) error
If you need mandatory parameters, add them as first arguments of the constructor before the variadic options
.
The main benefits of the Functional options idiom are :
- your API can grow over time without breaking existing code, because the constuctor signature stays the same when new options are needed.
- it enables the default use case to be its simplest: no arguments at all!
- it provides fine control over the initialization of complex values.
This technique was coined by Rob Pike and also demonstrated by Dave Cheney.
FooBar
is an opaque struct type, I don't see how users could create additional options. If FooBar
isn't opaque, then the benefits of functional options is moot, because users now have two ways of initialising a FooBar
: via a struct literal or via the factory function ("constructor"). My conclusion is that optional options work best when the set of options is meant to be closed and controlled by the package author. –
Disdainful Foobar
type, the library user can create an option by calling OptionTemperature
e.g. opt := OptionTemperature(27)
–
Bly FooBar
is opaque, users cannot create a new OptionFooBar
value that does anything useful. –
Disdainful OptionTemperatureForFoo
and OptionTemperatureForBar
? Doesn't that make the constructor become something nasty like NewFoo(OptionTemperatureForFoo(100), OptionReadonlyFlagForFoo(), OptionOtherForFoo())
? How could this be improved? –
Sidonia Neither optional parameters nor function overloading are supported in Go. Go does support a variable number of parameters: Passing arguments to ... parameters
You can pass arbitrary named parameters with a map. You will have to assert types with "aType = map[key].(*foo.type)
" if the parameters have non-uniform types.
type varArgs map[string]interface{}
func myFunc(args varArgs) {
arg1 := "default"
if val, ok := args["arg1"]; ok {
arg1 = val.(string)
}
arg2 := 123
if val, ok := args["arg2"]; ok {
arg2 = val.(int)
}
fmt.Println(arg1, arg2)
}
func Test_test() {
myFunc(varArgs{"arg1": "value", "arg2": 1234})
}
No -- neither. Per the Go for C++ programmers docs,
Go does not support function overloading and does not support user defined operators.
I can't find an equally clear statement that optional parameters are unsupported, but they are not supported either.
So I feel like I'm way late to this party but I was searching to see if there was a better way to do this than what I already do. This kinda solves what you were trying to do while also giving the concept of an optional argument.
package main
import "fmt"
type FooOpts struct {
// optional arguments
Value string
}
func NewFoo(mandatory string) {
NewFooWithOpts(mandatory, &FooOpts{})
}
func NewFooWithOpts(mandatory string, opts *FooOpts) {
if (&opts) != nil {
fmt.Println("Hello " + opts.Value)
} else {
fmt.Println("Hello")
}
}
func main() {
NewFoo("make it work please")
NewFooWithOpts("Make it work please", &FooOpts{Value: " World"})
}
Update 1:
Added a functional example to show functionality versus the sample
nil
the options to use defaults. Also the options can be documented in their own struct and you can create predefine sets of options. I've seen this in GitHub client library and go-cache library among others. –
Randeerandel Go doesn’t support optional parameters , default values and function overloading but you can use some tricks to implement the same.
Sharing one example where you can have different number and type of arguments in one function. It’s a plain code for easy understanding you need to add error handling and some logic.
func student(StudentDetails ...interface{}) (name string, age int, area string) {
age = 10 //Here Age and area are optional params set to default values
area = "HillView Singapore"
for index, val := range StudentDetails {
switch index {
case 0: //the first mandatory param
name, _ = val.(string)
case 1: // age is optional param
age, _ = val.(int)
case 2: //area is optional param
area, _ = val.(string)
}
}
return
}
func main() {
fmt.Println(student("Aayansh"))
fmt.Println(student("Aayansh", 11))
fmt.Println(student("Aayansh", 15, "Bukit Gombak, Singapore"))
}
You could use pointers and leave them nil if you don't want to use them:
func getPosts(limit *int) {
if optParam != nil {
// fetch posts with limit
} else {
// fetch all posts
}
}
func main() {
// get Posts, limit by 2
limit := 2
getPosts(&limit)
// get all posts
getPosts(nil)
}
iota
and "auto incremented" constants, in which case good luck with unadressable constants (because of course constants are magic and don't have a memory address) –
Katusha You can encapsulate this quite nicely in a func similar to what is below.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Println(prompt())
}
func prompt(params ...string) string {
prompt := ": "
if len(params) > 0 {
prompt = params[0]
}
reader := bufio.NewReader(os.Stdin)
fmt.Print(prompt)
text, _ := reader.ReadString('\n')
return text
}
In this example, the prompt by default has a colon and a space in front of it . . .
:
. . . however you can override that by supplying a parameter to the prompt function.
prompt("Input here -> ")
This will result in a prompt like below.
Input here ->
Go language does not support method overloading, but you can use variadic args just like optional parameters, also you can use interface{} as parameter but it is not a good choice.
I am a little late, but if you like fluent interface you might design your setters for chained calls like this:
type myType struct {
s string
a, b int
}
func New(s string, err *error) *myType {
if s == "" {
*err = errors.New(
"Mandatory argument `s` must not be empty!")
}
return &myType{s: s}
}
func (this *myType) setA (a int, err *error) *myType {
if *err == nil {
if a == 42 {
*err = errors.New("42 is not the answer!")
} else {
this.a = a
}
}
return this
}
func (this *myType) setB (b int, _ *error) *myType {
this.b = b
return this
}
And then call it like this:
func main() {
var err error = nil
instance :=
New("hello", &err).
setA(1, &err).
setB(2, &err)
if err != nil {
fmt.Println("Failed: ", err)
} else {
fmt.Println(instance)
}
}
This is similar to the Functional options idiom presented on @Ripounet answer and enjoys the same benefits but has some drawbacks:
- If an error occurs it will not abort immediately, thus, it would be slightly less efficient if you expect your constructor to report errors often.
- You'll have to spend a line declaring an
err
variable and zeroing it.
There is, however, a possible small advantage, this type of function calls should be easier for the compiler to inline but I am really not a specialist.
I ended up using a combination of a structure of params and variadic args. This way, I didn't have to change the existing interface which was consumed by several services and my service was able to pass additional params as needed. Sample code in golang playground: https://play.golang.org/p/G668FA97Nu
Another possibility would be to use a struct which with a field to indicate whether its valid. The null types from sql such as NullString are convenient. Its nice to not have to define your own type, but in case you need a custom data type you can always follow the same pattern. I think the optional-ness is clear from the function definition and there is minimal extra code or effort.
As an example:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}
© 2022 - 2024 — McMap. All rights reserved.