How to properly use variadic args in Golang? - go

I am complete beginner with Go and I am trying to pass variadic args to encodeit method as a string that will hash the string, otherwise pass an empty string. I wan't to print out hashed string.
I have tried multiple things, but could not get it to work.
package main
import(
"crypto/sha512"
"encoding/hex"
"fmt"
)
func encodeit(content string) string {
sha_512 := sha512.New()
sha_512.Write([]byte(content))
contentH := sha_512.Sum(nil)
contentHash := hex.EncodeToString([]byte(contentH))
return contentHash
}
func some(payload ...string) {
if len(payload) == 1 {
contentHash := encodeit(payload)
} else {
contentHash := encodeit("")
}
return contentHash
}
func main() {
fmt.Println(some(`{"stockSymbol": "TSLA"}`))
}
Here is the error log
# command-line-arguments
.\stackOverflow.go:19:26: cannot use payload (type []string) as type string in argument to encodeit
.\stackOverflow.go:23:2: too many arguments to return
.\stackOverflow.go:23:9: undefined: contentHash
.\stackOverflow.go:27:18: some("{\"stockSymbol\": \"TSLA\"}") used as value

payload becomes an array of strings ([]string) when using an ellipsis (...). It can be iterated on using a key,value for loop:
func printEncoded(payload ...string) {
for i, value := range payload {
fmt.Println(i, encode(value))
}
}
Use printEncoded("TSLA","AMD","DOW") and you won't have to create your own []string array as an argument ([]string{"TSLA","AMD","DOW"}).
You're also going to want to take a look at the JSON package for parsing: {"stockSymbol": "TSLA"}
Fixed Playground

check your func return value:
func some(payload ...string) string
you missed the return type string.

Related

How should I pass param in golang?

I have the follow whole codes:
I hope that string convert map in golang, and use golang reflect.
The following code have simpled from my project.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
jsonStr := `{"name": "thinkerou", "age": 31, "balance": 3.14}`
var a map[string]interface{}
var value reflect.Value = reflect.ValueOf(&a)
// call function and pass param
f(jsonStr, value)
// print result
fmt.Println(value.Kind(), value.Interface())
}
func f(v string, value reflect.Value) {
personMap := make(map[string]interface{})
err := json.Unmarshal([]byte(v), &personMap)
if err != nil {
panic(err)
}
value = reflect.Indirect(value)
value = reflect.MakeMap(value.Type())
for k, v := range personMap {
// set key/value
value.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v))
}
// print result
fmt.Println(value.Kind(), value.Interface())
}
and run it and will get the result:
map map[age:31 balance:3.14 name:thinkerou]
ptr &map[]
I hope the follow result:
map map[age:31 balance:3.14 name:thinkerou]
map map[age:31 balance:3.14 name:thinkerou]
How should I pass reflect.Value param? thanks!
You should be able to get your map from the interface, using type assertion:
a := i.(map[string]interface{})
See "Convert Value type to Map in Golang?"
I have modified your code here.
Note that I don't try to mutate the f(value) argument, but I return it instead.
func f(v string, value reflect.Value) reflect.Value {
...
return value
}
So the code becomes:
value = f(jsonStr, value)
fmt.Println(value.Kind(), value.Interface().(map[string]interface{}))

Can I make an optional return value in golang? [duplicate]

In Go, the following works (note one use of the map has one return, the other has two returns)
package main
import "fmt"
var someMap = map[string]string { "some key": "hello" }
func main() {
if value, ok := someMap["some key"]; ok {
fmt.Println(value)
}
value := someMap["some key"]
fmt.Println(value)
}
However, I have no idea how to do this same thing with my own function. Is it possible to have similar behavior with an optional return like map?
For example:
package main
import "fmt"
func Hello() (string, bool) {
return "hello", true
}
func main() {
if value, ok := Hello(); ok {
fmt.Println(value)
}
value := Hello()
fmt.Println(value)
}
Wont compile (due to the error multiple-value Hello() in single-value context) ... is there a way to make this syntax work for the function Hello()?
map is different because it is a built-in type and not a function. The 2 forms of accessing an element of a map is specified by the Go Language Specification: Index Expressions and backed by the compiler.
With functions you can't do this. If a function has 2 return values, you have to "expect" both of them or none at all.
However you are allowed to assign any of the return values to the Blank identifier:
s, b := Hello() // Storing both of the return values
s2, _ := Hello() // Storing only the first
_, b3 := Hello() // Storing only the second
You can also choose not to store any of the return values:
Hello() // Just executing it, but storing none of the return values
Note: you could also assign both of the return values to the blank identifier, although it has no use (other than validating that it has exactly 2 return values):
_, _ = Hello() // Storing none of the return values; note the = instead of :=
You can also try these on the Go Playground.
Helper function
If you use it many times and you don't want to use the blank identifier, create a helper function which discards the 2nd return value:
func Hello2() string {
s, _ := Hello()
return s
}
And now you can do:
value := Hello2()
fmt.Println(value)
Go 1.18 generics update: Go 1.18 adds generics support, it is now possible to write a generic First() function which discards the second (or any further) return values:
func First[T any](first T, _ ...any) T {
return first
}
This is available in github.com/icza/gog, as gog.First() (disclosure: I'm the author).
Using it:
value := First(Hello())
fmt.Println(value)
In addition to the explanation of #icza:
I don't recommend using a helper function there. Especially if the Hello function is your own function.
However, if you can't control it, then it's fine to use a helper.
If it's your own function, it's better to change the signature of your function. Probably, you made a design mistake somewhere.
You can also do this:
package main
import "fmt"
func Hello() (string, bool) {
return "hello", true
}
func main() {
// Just move it one line above: don't use a short-if
value, ok := Hello()
if ok {
fmt.Println(value)
}
}

Passing in a type variable into function

I'm trying to achieve a type assertion by passing in a type into a function. In other words, I'm trying to achieve something like this:
// Note that this is pseudocode, because Type isn't the valid thing to use here
func myfunction(mystring string, mytype Type) {
...
someInterface := translate(mystring)
object, ok := someInterface.(mytype)
... // Do other stuff
}
func main() {
// What I want the function to be like
myfunction("hello world", map[string]string)
}
What's the proper function declaration I need to use in myfunction, to successfully perform the type assertion in myfunction?
#hlin117,
Hey, if I understood your question correctly and you need to compare the types, here's what you can do:
package main
import (
"fmt"
"reflect"
)
func myfunction(v interface{}, mytype interface{}) bool {
return reflect.TypeOf(v) == reflect.TypeOf(mytype)
}
func main() {
assertNoMatch := myfunction("hello world", map[string]string{})
fmt.Printf("%+v\n", assertNoMatch)
assertMatch := myfunction("hello world", "stringSample")
fmt.Printf("%+v\n", assertMatch)
}
The approach is to use a sample of the type you'd like to match.

How I can get return value based on argument type?

When I define function
func test(a int, b int) int {
//bla
}
I must set arguments and return value types. How I can return value based on argument type, ex
func test(argument type) type {
//if argument type == string, must return string
//or else if argument int, must return integer
}
Can I do this and how?
Go lacks generics, (not going to argue this point one way or the other), you can achieve this by passing interface{} to functions and then doing a type assertion on the other side.
package main
import "fmt"
func test(t interface{}) interface{} {
switch t.(type) {
case string:
return "test"
case int:
return 54
}
return ""
}
func main() {
fmt.Printf("%#v\n", test(55))
fmt.Printf("%#v", test("test"))
}
You will have to type assert the value you get out
v := test(55).(int)
Go does not yet have generics like C# or Java.
It does have an empty interface (interface{})
Here is code that I believe answers your question, if I understood it correctly:
package main
import (
"fmt"
"reflect"
)
type generic interface{} // you don't have to call the type generic, you can call it X
func main() {
n := test(10) // I happen to pass an int
fmt.Println(n)
}
func test(arg generic) generic {
// do something with arg
result := arg.(int) * 2
// check that the result is the same data type as arg
if reflect.TypeOf(arg) != reflect.TypeOf(result) {
panic("type mismatch")
}
return result;
}

Chaining functions in Go?

I tried doing this:
package main
import (
"fmt"
"strings"
)
type String string
func (s *String) tolower() String {
*s = String(strings.ToLower(string(*s)))
return *s
}
func (s *String) toupper() String {
*s = String(strings.ToUpper(string(*s)))
return *s
}
func main() {
var s String = "ASDF"
(s.tolower()).toupper() // this fails
// s.toupper();s.tolower(); // this works
// s.tolower().toupper() // this fails too
fmt.Println(s)
}
But I got these errors:
prog.go:30: cannot call pointer method on s.tolower()
prog.go:30: cannot take the address of s.tolower()
Program exited.
Why can't I make this chain work?
This works:
package main
import (
"fmt"
"strings"
)
type String string
func (s *String) tolower() *String {
*s = String(strings.ToLower(string(*s)))
return s
}
func (s *String) toupper() *String {
*s = String(strings.ToUpper(string(*s)))
return s
}
func main() {
var s String = "ASDF"
(s.tolower()).toupper()
s.toupper();
s.tolower();
s.tolower().toupper()
fmt.Println(s)
}
Your return type is of String, for functions defined on pointers to String. It wouldn't make sense to be able to chain them.
tolower() and toupper() have pointer-to-String as the receivers, but they are returning String (not pointer-to-String).
You can fix this by changing one or the other.
e.g. change the signature of the function to either:
func (s *String) toupper() *String
or
func (s String) toupper() String
(see: http://play.golang.org/p/FaCD8AQtIX)
When you call a method with a pointer receiver on a variable (s in your example), then an address of that value will be taken automatically. So, you are basically calling (&s).toupper(). This mechanism works for all values that are addressable.
Return values of functions are not addressable unless you store them in a variable (so that they have a permanent place in the current stack frame or the heap).
I would recommend the following API, because it looks like the user of your string type is supposed to work with String and not *String. Therefore it makes sense to design a consistent API that also uses String to avoid confusion. Passing a string by value is extremely fast anyway, because they are implemented as pointers to immutable arrays internally:
func (s String) tolower() String {
return String(strings.ToLower(string(s)))
}
This method does not need a pointer receiver, because it doesn't modify the current string. It returns a new string instead. You can also easily chain those methods.
Alternatively, you can implement the methods this way:
func (s *String) tolower() *String {
*s = String(strings.ToLower(string(*s)))
return s
}
In this case, you keep returning the same pointer. So, in order to call (s.tolower()).toupper() you need to be able to take the address of s which is possible since you have assigned it to a variable. Then all further method calls in the chain are also possible, because you call them with a pointer to your initial variable. This differs from your attempt of chaining methods were each method call must have taken the address of a temporary variable in order to modify it (which isn't very useful).
maybe you can try this project: https://github.com/Laisky/go-chaining
import "github.com/Laisky/go-chaining"
func toLower(c *chaining.Chain) (interface{}, error) {
v := c.GetString()
return strings.ToLower(v), nil
}
func toUpper(c *chaining.Chain) (interface{}, error) {
v := c.GetString()
return strings.ToUpper(v), nil
}
func TestChainWithString(t *testing.T) {
f := func() (string, error) { return "aBcD", nil }
r := chaining.New(f()).
Next(toLower).
Next(toUpper)
expectVal := "ABCD"
if r.GetString() != expectVal {
t.Errorf("expect %v, got %v", expectVal, r.GetString())
}
}

Resources