Is it possible, and how to make one-line (or just short) var assignment with such logic:
a := b (or if b is false, nil or undifined, then a equals to) "hello"
I try to make:
a := b | "hello"
but getting an error: "operator | not defined on string".
so I am from JS, and trying to implement:
const a = b || "hello";
but in a "Go-way"
As it is statically typed Lang, I meant that if b == "" (empty string)
There is no Go syntax like this. Use an if statement.
The operands of | must be integers. The operands of || must be booleans.
What's more, a string cannot be false or nil.
Given that OP's actual goal is to supply a default for an environment variable, the test should be on the boolean return value from os.LookupEnv, not the string value. Use a function like this:
func getenv(key string, def string) string {
val, ok := os.LookupEnv(key)
if !ok {
val = def
}
return val
}
I had a similar desire when defining default values to Env variables.
The way I solved it was to define a function to do it, like this:
func getenv(key, fallback string) string {
value := os.Getenv(key)
if len(value) == 0 {
return fallback
}
return value
}
When I call this function, it looks just as concise as a "or" would be:
port := getenv("PORT", "8000")
You could define your own functions to your use cases.
I think doing your own function is great but golang has its own way for managing flags here is an example
import (
"flag"
)
func main() {
env := flag.String("env", "dev", "description")
}
so here the default value for env is dev and you run and change it by
go run main.go -env=prod
regards
Related
I'm trying to implement a default value according to the option 1 of the post Golang and default values. But when I try to do go install the following error pops up in the terminal:
not enough arguments in call to test.Concat1
have ()
want (string)
Code:
package test
func Concat1(a string) string {
if a == "" {
a = "default-a"
}
return fmt.Sprintf("%s", a)
}
// other package
package main
func main() {
test.Concat1()
}
Thanks in advance.
I don't think what you are trying to do will work that way. You may want to opt for option #4 from the page you cited, which uses variadic variables. In your case looks to me like you want just a string, so it'd be something like this:
func Concat1(a ...string) string {
if len(a) == 0 {
return "a-default"
}
return a[0]
}
Go does not have optional defaults for function arguments.
You may emulate them to some extent by having a special type
to contain the set of parameters for a function.
In your toy example that would be something like
type Concat1Args struct {
a string
}
func Concat1(args Concat1Args) string {
if args.a == "" {
args.a = "default-a"
}
return fmt.Sprintf("%s", args.a)
}
The "trick" here is that in Go each type has its respective
"zero value", and when producing a value of a composite type
using the so-called literal, it's possible to initialize only some of the type's fields, so in our example that would be
s := Concat1(Concat1Args{})
vs
s := Concat1(Concat1Args{"whatever"})
I know that looks clumsy, and I have showed this mostly for
demonstration purpose. In real production code, where a function
might have a dozen of parameters or more, having them packed
in a dedicate composite type is usually the only sensible way
to go but for a case like yours it's better to just explicitly
pass "" to the function.
Golang does not support default parameters. Accordingly, variadic arguments by themselves are not analogous. However, variadic functions with the use of error handling can 'resemble' the pattern. Try the following as a simple example:
package main
import (
"errors"
"log"
)
func createSeries(p ...int) ([]int, error) {
usage := "Usage: createSeries(<length>, <optional starting value>), length should be > 0"
if len(p) == 0 {
return nil, errors.New(usage)
}
n := p[0]
if n <= 0 {
return nil, errors.New(usage)
}
var base int
if len(p) == 2 {
base = p[1]
} else if len(p) > 2 {
return nil, errors.New(usage)
}
vals := make([]int, n)
for i := 0; i < n; i++ {
vals[i] = base + i
}
return vals, nil
}
func main() {
answer, err := createSeries(4, -9)
if err != nil {
log.Fatal(err)
}
log.Println(answer)
}
Default parameters work differently in Go than they do in other languages. In a function there can be one ellipsis, always at the end, which will keep a slice of values of the same type so in your case this would be:
func Concat1(a ...string) string {
but that means that the caller may pass in any number of arguments >= 0. Also you need to check that the arguments in the slice are not empty and then assign them yourself. This means they do not get assigned a default value through any kind of special syntax in Go. This is not possible but you can do
if a[0] == "" {
a[0] = "default value"
}
If you want to make sure that the user passes either zero or one strings, just create two functions in your API, e.g.
func Concat(a string) string { // ...
func ConcatDefault() string {
Concat("default value")
}
How do you assign a default value if an environment variable isn't set in Go?
In Python I could do mongo_password = os.getenv('MONGO_PASS', 'pass') where pass is the default value if MONGO_PASS env var isn't set.
I tried an if statement based on os.Getenv being empty, but that doesn't seem to work due to the scope of variable assignment within an if statement. And I'm checking for multiple env var's, so I can't act on this information within the if statement.
There's no built-in to fall back to a default value,
so you have to do a good old-fashioned if-else.
But you can always create a helper function to make that easier:
func getenv(key, fallback string) string {
value := os.Getenv(key)
if len(value) == 0 {
return fallback
}
return value
}
Note that as #michael-hausenblas pointed out in a comment,
keep in mind that if the value of the environment variable is really empty, you will get the fallback value instead.
Even better as #ĆukaszWojciechowski pointed out, using os.LookupEnv:
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
What you're looking for is os.LookupEnv combined with an if statement.
Here is janos's answer updated to use LookupEnv:
func getEnv(key, fallback string) string {
value, exists := os.LookupEnv(key)
if !exists {
value = fallback
}
return value
}
Go doesn't have the exact same functionality as Python here; the most idiomatic way to do it though, I can think of, is:
mongo_password := "pass"
if mp := os.Getenv("MONGO_PASS"); mp != "" {
mongo_password = mp
}
To have a clean code I do this:
myVar := getEnv("MONGO_PASS", "default-pass")
I defined a function that is used in the whole app
// getEnv get key environment variable if exist otherwise return defalutValue
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if len(value) == 0 {
return defaultValue
}
return value
}
Had the same question as the OP and found someone encapsulated the answers from this thread into a nifty library that is fairly simple to use, hope this help others!
https://github.com/caarlos0/env
For more complex application you can use tooling such as viper, which allows you to set global custom default values, parse configuration files, set a prefix for your app's env var keys (to ensure consistency and name spacing of env var configurations) and many other cool features.
Sample code:
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.AutomaticEnv() // read value ENV variable
// Set default value
viper.SetEnvPrefix("app")
viper.SetDefault("linetoken", "DefaultLineTokenValue")
// Declare var
linetoken := viper.GetString("linetoken")
fmt.Println("---------- Example ----------")
fmt.Println("linetoken :", linetoken)
}
I also had the same problem and I just created a small package called getenvs exactly to answer this problem.
Getenvs supports string, bool, int and float and it can be used like below:
package main
import (
"fmt"
"gitlab.com/avarf/getenvs"
)
func main() {
value := getenvs.GetEnvString("STRING_GETENV", "default-string-value")
bvalue, _ := getenvs.GetEnvBool("BOOL_GETENV", false)
ivalue, _ := getenvs.GetEnvInt("INT_GETENV", 10)
fmt.Println(value)
fmt.Println(bvalue)
fmt.Println(ivalue)
}
In case you are OK with adding little dependency you can use something like https://github.com/urfave/cli
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
EnvVar: "APP_LANG",
},
}
app.Run(os.Args)
}
I have a function that solves the problem of Go not allowing for the setting of default values in method declarations. I want to make it just a little bit better by allowing for a variable number of return variables. I understand that I can allow for an array of interfaces as a return type and then create an interface array with all the variables to return, like this:
func SetParams(params []interface{}, args ...interface{}) (...[]interface{}) {
var values []interface{}
for i := range params {
var value interface{}
paramType := reflect.TypeOf(params[i])
if len(args) < (i + 1) {
value = params[i]
} else {
argType := reflect.TypeOf(args[i])
if paramType != argType {
value = params[i]
}
value = args[i]
}
values = append(values, value)
}
return values
}
This is an example of a method you want to define default values for. You build it as a variadic function (allowing a variable number of parameters) and then define the default values of the specific params you are looking for inside the function instead of in the declaration line.
func DoSomething(args ...interface{}) {
//setup default values
str := "default string 1"
num := 1
str2 := "default string 2"
//this is fine
values := SetParams([]interface{str, num, str2}, args)
str = values[0].(string)
num = values[1].(int)
str = values[2].(string)
//I'd rather support this
str, num, str2 = SetParams(params, args)
}
I understand that
[]interface{str, num, str2}
in the above example is not syntactically correct. I did it that way to simplify my post. But, it represents another function that builds the array of interfaces.
I would like to support this:
str, num, str2 = SetParams(params, args)
instead of having to do this:
values := SetParams([]interface{str, num, str2}, args)
str = values[0].(string)
num = values[1].(int)
str = values[2].(string)
Any advice? Help?
Please don't write horrible (and ineffective due to reflect) code to solve nonexistent problem.
As was indicated in comments, turning a language into
one of your previous languages is indeed compelling
after a switch, but this is counterproductive.
Instead, it's better to work with the idioms and approaches
and best practices the language provides --
even if you don't like them (yet, maybe).
For this particular case you can roll like this:
Make the function which wants to accept
a list of parameters with default values
accept a single value of a custom struct type.
For a start, any variable of such type, when allocated,
has all its fields initialized with the so-called "zero values"
appropriate to their respective types.
If that's enough, you can stop there: you will be able
to pass values of your struct type to your functions
by producing them via literals right at the call site --
initializing only the fields you need.
Otherwise have pre-/post- processing code which
would provide your own "zero values" for the fields
you need.
Update on 2016-08-29:
Using a struct type to simulate optional parameters
using its fields being assigned default values which happen
to be Go's native zero values for their respective data types:
package main
import (
"fmt"
)
type params struct {
foo string
bar int
baz float32
}
func myfun(params params) {
fmt.Printf("%#v\n", params)
}
func main() {
myfun(params{})
myfun(params{bar: 42})
myfun(params{foo: "xyzzy", baz: 0.3e-2})
}
outputs:
main.params{foo:"", bar:0, baz:0}
main.params{foo:"", bar:42, baz:0}
main.params{foo:"xyzzy", bar:0, baz:0.003}
As you can see, Go initializes the fields of our params type
with the zero values appropriate to their respective types
unless we specify our own values when we define our literals.
Playground link.
Providing default values which are not Go-native zero values for
the fields of our custom type can be done by either pre-
or post-processing the user-submitted value of a compound type.
Post-processing:
package main
import (
"fmt"
)
type params struct {
foo string
bar int
baz float32
}
func (pp *params) setDefaults() {
if pp.foo == "" {
pp.foo = "ahem"
}
if pp.bar == 0 {
pp.bar = -3
}
if pp.baz == 0 { // Can't really do this to FP numbers; for demonstration purposes only
pp.baz = 0.5
}
}
func myfun(params params) {
params.setDefaults()
fmt.Printf("%#v\n", params)
}
func main() {
myfun(params{})
myfun(params{bar: 42})
myfun(params{foo: "xyzzy", baz: 0.3e-2})
}
outputs:
main.params{foo:"ahem", bar:-3, baz:0.5}
main.params{foo:"ahem", bar:42, baz:0.5}
main.params{foo:"xyzzy", bar:-3, baz:0.003}
Playground link.
Pre-processing amounts to creating a "constructor" function
which would return a value of the required type pre-filled
with the default values your choice for its fieldsâsomething
like this:
func newParams() params {
return params{
foo: "ahem",
bar: -3,
baz: 0.5,
}
}
so that the callers of your function could call newParams(),
tweak its fields if they need and then pass the resulting value
to your function:
myfunc(newParams())
ps := newParams()
ps.foo = "xyzzy"
myfunc(ps)
This approach is maybe a bit more robust than post-processing but
it precludes using of literals to construct the values to pass to
your function right at the call site which is less "neat".
Recently I was playing with anonymous functions in Go and implemented an example which accepts and returns undefined parameters:
func functions() (funcArray []func(args ... interface{}) (interface{}, error)) {
type ret struct{
first int
second string
third bool
}
f1 := func(args ... interface{}) (interface{}, error){
a := args[0].(int)
b := args[1].(int)
return (a < b), nil
}
funcArray = append(funcArray , f1)
f2 := func(args ... interface{}) (interface{}, error){
return (args[0].(string) + args[1].(string)), nil
}
funcArray = append(funcArray , f2)
f3 := func(args ... interface{}) (interface{}, error){
return []int{1,2,3}, nil
}
funcArray = append(funcArray , f3)
f4 := func(args ... interface{}) (interface{}, error){
return ret{first: 1, second: "2", third: true} , nil
}
funcArray = append(funcArray , f4)
return funcArray
}
func main() {
myFirst_Function := functions()[0]
mySecond_Function := functions()[1]
myThird_Function := functions()[2]
myFourth_Function := functions()[3]
fmt.Println(myFirst_Function(1,2))
fmt.Println(mySecond_Function("1","2"))
fmt.Println(myThird_Function())
fmt.Println(myFourth_Function ())
}
I hope it helps you.
https://play.golang.org/p/d6dSYLwbUB9
I'm quite confused about the .(type) syntax for interface variables. Is it possible to use it like this:
var a,b interface{}
// some code
if first.(type) == second.(type) {
}
or is reflect.TypeOf() the only option to check if the underlying types of a and b are the same type? What comparison am I making in the code above?
someInterface.(type) is only used in type switches. In fact if you tried to run that you'd see that in the error message.
func main() {
var a, b interface{}
a = 1
b = 1
fmt.Println(a.(type) == b.(type))
}
prog.go:10: use of .(type) outside type switch
What you could do instead is a.(int) == b.(int), which is really no different from int(a) == int(b)
func main() {
var a, b interface{}
a = 1
b = 1
fmt.Println(a.(int) == b.(int))
}
true
func isType(a, b interface{}) bool {
return fmt.Sprintf("%T", a) == fmt.Sprintf("%T", b)
}
The "%T" fmt option uses reflection under the hood, which would make the above statement practically that same as:
func isType(a, b interface{}) bool {
return reflect.TypeOf(a) == reflect.TypeOf(b)
}
Either one would work, and won't cause a panic trying to utilize any kind of type assertion like some of the other suggestions.
You'd need to specify the type. That syntax is used to make type assertions about interfaces, not about checking the specific type.
You'll have to use reflect.TypeOf for that.
You can view this answer for a proper use of type assertions.
Please try this:
for key, value := range data {
switch v := value.(type) {
case string:
fmt.Println(key, value, "(string)")
case float64:
fmt.Println(key, value, "(float64)")
case []interface{}:
fmt.Println(key, value, "(interface)")
}
}
...
and you can use for all types in any interface...
in go tutorial following code is often seen:
a := foo()
b, c := foo()
or actually what I see is:
m["Answer"] = 48
a := m["Answer"]
v, ok := m["Answer"]
how many foo() is defined?
Is it two, one with one return type, another with two return type?
Or just one foo() with two return type defined, and somehow magically when only need one return value (a := foo()), another return value is omitted?
I tried
package main
func main() {
a := foo()
a = 1
}
func foo() (x, y int) {
x = 1
y = 2
return
}
func foo() (y int) {
y = 2
return
}
But I got error message foo redeclared in this block
While some built in operations support both single and multiple return value modes (like reading from a map, type assertions, or using the range keyword in loops), this feature is not available to user defined functions.
If you want two versions of a function with different return values, you will need to give them different names.
The Effective Go tutorial has some good information on this.
Basically, a function defines how many values it returns with it's return statement, and it's function signature.
To ignore one or more of the returned values you should use the Blank Identifier, _(Underscore).
For example:
package main
import "fmt"
func singleReturn() string {
return "String returned"
}
func multiReturn() (string, int) {
return "String and integer returned", 1
}
func main() {
s := singleReturn()
fmt.Println(s)
s, i := multiReturn()
fmt.Println(s, i)
}
Playground
The v, ok := m["answer"] example you've given is an example of the "comma, ok" idiom (Also described in the Effective Go link above). The linked documentation uses type assertions as an example of it's use:
To extract the string we know is in the value, we could write:
str := value.(string)
But if it turns out that the value does not contain a string, the program will crash with a run-time error. To guard against that, use the "comma, ok" idiom to test, safely, whether the value is a string:
str, ok := value.(string)
if ok {
fmt.Printf("string value is: %q\n", str)
} else {
fmt.Printf("value is not a string\n")
}
If the type assertion fails, str will still exist and be of type string, but it will have the zero value, an empty string.