Why can't I access map key with an int? - go

I create a map like so:
board := make(map[int]map[string]string)
I add some numbers to it so data is formatted like follows.
1 : map("a", "b" ..)
I then pass in a position. "a1" and this is where I hit a wall.
func (checkers *Checkers) setPiece(piece string, coordinates string) {
lett := string(coordinates[0]);
num, err := strconv.ParseInt(string(coordinates[1]), 0, 64)
if err != nil {
panic("Invalid coordinate format")
}
row := checkers.board[num]
}
I get the follow error: 'cannot use num (type int64) as type int in map index'
Why do I get this error? How do I access a key in the map?

You just have to convert from int64 to int. like so:
checkers.board[int(num)]
However, if all you want is to parse an int out of a string, you should use strconv.AtoI for that. It will return (int, error) so you don't have to convert it. Also, keep in mind that the way your code is currently written will not work for 2-digit numbers or 2-letter prefixes. This may be by design.

Use
num, err := strconv.Atoi(string(coordinates[1]))
which returns an int.
Package strconv
func Atoi
func Atoi(s string) (i int, err error)
Atoi is shorthand for ParseInt(s, 10, 0).

Related

Assign empty slice without referring to its type?

My code calls a library function which looks roughly like this:
func Search() ([]myLibrary.SomeObject, error) {
var results []apiv17.SomeObject
// ...
if (resultsFound) {
results = append(results, someResult)
}
return results
}
...and my code calls it and then marshals it to JSON.
results, err := myLibrary.Search()
bytes, err := json.Marshal(results)
Now the problem is that because of the way the Search function is written (and let's assume we can't change it), it'll return an uninitialized nil slice if there are no results. And unfortunately, there is no way to configure encoding/json to encode nil slices as [] (see e.g. this proposal with ongoing discussion).
Explicitly checking for nil solves the problem:
results, err := myLibrary.Search()
if results == nil {
results = []apiv17.SomeObject{}
}
bytes, err := json.Marshal(results)
...but it also adds an explicit dependency on the return type, apiv17.SomeObject. That's inconvenient because that type frequently changes in the library. E.g. in the next library version it might be apiv18.SomeObject.
With the nil check above, I'll have to update my code every time that happens.
Is there any way to avoid this and assign an empty, non-nil slice to the variable without explicitly referring to its type? Something like this:
results = [](type of results){}
Go 1.18
You can use a generic function that captures the slice's base type and returns a slice of length zero:
func echo[T any](v []T) []T {
return make([]T, 0)
}
func main() {
n := foo.GetFooBar()
if n == nil {
n = echo(n) // no need to refer to apiv17 here
}
bytes, _ := json.Marshal(n)
fmt.Println(string(bytes)) // prints []
}
The purpose of requiring a regular argument v []T in echo is to allow type inference to unify the slice []apiv17.SomeObject with the argument []T and infer T as the base type apiv17.SomeObject, so that you can call it just as echo(n) and no explicit type parameter.
The package apiv17 is of course known at compile time because it's transitively imported via myPackage, so you can take advantage of this and type inference to avoid adding an explicit import statement for apiv17.
This is how it looks like on the multi-file playground: https://go.dev/play/p/4ycTkaGLFpo
The type is declared in bar package, but main only imports play.ground/foo and only uses foo.GetFooBar.
Go 1.17 and below
Reflection. Just change the echo function from above to taking an interface{} argument (there's no any in Go 1.17, remember?) and do the deed with reflect.MakeSlice:
func set(v interface{}) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr {
panic("not a ptr")
}
reflect.Indirect(rv).Set(reflect.MakeSlice(rv.Type().Elem(), 0, 0))
}
Then pass a pointer to the slice, so that you can set its value with reflection.
func main() {
n := foo.GetFooBar()
if n == nil {
set(&n)
}
fmt.Printf("type: %T, val: %v, is nil: %t\n", n, n, n == nil)
// type: []bar.FooBar, val: [], is nil: false
bytes, _ := json.Marshal(n)
fmt.Println(string(bytes)) // prints [] again
}
Go 1.17 playground: https://go.dev/play/p/4jMkr22LMF7?v=goprev
The other answer describes how to create an empty slice.
But you can solve your original issue much simpler: if results is nil, you don't need to create a empty slice, regardless of whatever element type it would have, the JSON marshaling would be [] anyway. So if results is nil, no need to call json.Marshal(), just "output" []:
results, err := myLibrary.Search()
var bytes []byte
if results == nil {
bytes = []byte{'[', ']' } // JSON marshaling result is "[]"
} else {
bytes, err = json.Marshal(results)
// Handle error
}

Getting missing return at end of function while using return Golang

When i'm running my code, the output tells me that i'm missing a return statement even when i'm using one.
This is my code:
func uitchecken(product string, balance float64, voltarief float64, instaptarief float64) float64 {
// This part of the code makes a string into a float64
if s, err := strconv.ParseFloat(product, 64); err == nil {
result1 := s
result := balance - (result1 * voltarief) + instaptarief
return result
}
}
The idea of this code is to get 1 string and 3 float64 in it then do a little bit af math and returns the value in the main function.
If err != nil, your function literally returns nothing.
I would say, the code as presented in an ani-pattern in Go (though, it may indeed occasionally be useful) because a good style in Go is to have the main program flow to be "on the main line", like in
val, err := doSomething()
if err != nil {
// Handle the error out of the way
}
// "Normal" flow dealing with `val` is on the main line.
In your particular case, I cannot see why parsing of product may not fail.
If you are sure it cannot, make the code panic, like with:
func mustParseFloat(s string) float64 {
s, err := strconv.ParseFloat(product, 64)
if err != nil {
panic(err)
}
return s
}
func uitchecken(product string, balance float64, voltarief float64, instaptarief float64) float64 {
result1 := mustParseFloat(product)
return balance - (result1 * voltarief) + instaptarief
}
Still, given the names of your variables, I sense the "code smell" with your original example: it's strange to see the variable product contain a string which is needed to be parsed while the rest of the variables participating in the same calculation are float64s; it feels like you'd better try to parse that string somewhere way closer to the point it was received from the user (or the client)—with the possible parsing error checked and reported to that user/client right there.
In other words, it's a good engineering practice to first validate the user's input and then operate on the known-to-be-valid data.
Return after if
func uitchecken(product string, balance float64, voltarief float64, instaptarief float64) float64 {
// This part of the code makes a string into a float64
if s, err := strconv.ParseFloat(product, 64); err == nil {
result1 := s
result := balance - (result1 * voltarief) + instaptarief
return result
}
return something <<<<<<<<<<<<<<<<
}
```

Writing non-string value into io.Writer Write method

I'm trying to write non-string value into io.Writer like integer, float, slices, or even map[string]interface{}. I'm expecting the result written returned as expected type written. If I wrote int into the Write, then I will get integer type value after decoding the []byte returned. How to do it in Go?
What you're probably looking for is encoding/gob since that encoding retains the Go type information. Out of the box it supports some of the builtin Go types and some of the basic gob types. If you want to encode/decode types not supported out of the box by gob you can use the gob.Register function to register those types.
To encode:
var v interface{} = uint8(123)
if err := gob.NewEncoder(w).Encode(&v); err != nil {
panic(err)
}
Note that the above passes a value of type *interface{} to Encode, this is necessary if, at the other end, the decoder doesn't know the type beforehand and has to also use type *interface{} as the argument to Decode. If you have a scenario where the decoder knows the concrete type the of the incoming data then you can also pass a value of that concrete type to Encode.
To decode:
var v interface{}
if err := gob.NewDecoder(r).Decode(&v); err != nil {
panic(err)
}
fmt.Println(v) // output: 123
fmt.Printf("%T", v) // output: uint8
https://play.golang.org/p/cCtQse8BoqZ
This seems to do it:
package main
import "encoding/json"
func main() {
a := []interface{}{
31, 3.1, []int{12,31}, map[string]interface{}{"month": 12, "day": 31},
}
b, err := json.Marshal(a)
if err != nil {
panic(err)
}
println(string(b)) // [31,3.1,[12,31],{"day":31,"month":12}]
}
https://pkg.go.dev/encoding/json#Marshal

Convert binary value as string to uint32 in Golang

Hello i am trying to convert 00000000000000000000000000001011 to uint32 in golang using
var v = "00000000000000000000000000001011"
fmt.Printf("%T\n", v)
c := []byte(v)
u := binary.LittleEndian.Uint32(c)
However it is not working.
You can't use encoding/binary for this, as that is to serialize and deserialize the (memory) bytes of different values (e.g. numbers). What you have is the base 2 string representation of the number.
To get its integer value you have to parse it. For that, use strconv.ParseUint():
s := "00000000000000000000000000001011"
u, err := strconv.ParseUint(s, 2, 32)
if err != nil {
panic(err)
}
fmt.Println(u)
This outputs (try it on the Go Playground):
11
Note that strconv.ParseUint() returns a value of type uint64, so if you need uint32, you have to manually convert it, e.g.:
u32 := uint32(u)
There are more options for parsing numbers from strings, for an overview, check Convert string to integer type in Go?
For example,
package main
import (
"fmt"
"strconv"
)
func main() {
s := "00000000000000000000000000001011"
fmt.Println(s)
u64, err := strconv.ParseUint(s, 2, 32)
u32 := uint32(u64)
if err == nil {
fmt.Println(u32)
}
}
Playground: https://play.golang.org/p/yiicgWsb7B_M
Output:
00000000000000000000000000001011
11

Go precise decimals with mgo

I am writing an application where I use money and want very accurate numbers. I am also using mgo to store the results after some application. I was wondering if there was a way for me to use math.Rat or godec in a struct and have it store as a number in mgo?
This is the kind of code i was hoping to run:
package main
import(
"fmt"
"math/big"
"labix.org/v2/mgo"
)
var mgoSession *mgo.Session
type Test struct{
Budget big.Rat
}
func MongoLog(table string, pointer interface{}) {
err := mgoSession.DB("db_log").C(table).Insert(pointer)
if err != nil {
panic(err)
}
}
func main(){
var err error
mgoSession, err = mgo.Dial("localhost:27017")
defer mgoSession.Close()
if err != nil {
panic(err)
}
cmp := big.NewRat(1, 100000)
var test = Test{Budget : *big.NewRat(5, 10)}
MongoLog("test", &test)
for i := 0; i < 20; i++{
fmt.Printf("Printf: %s\n", test.Budget.FloatString(10))
fmt.Println("Println:", test.Budget, "\n")
test.Budget.Sub(&test.Budget, cmp)
// test.Budget = test.Budget - cpm
}
MongoLog("test", &test)
}
big.Rat is basically a pair of unexported int big.Int values describing the numerator and denominator of a rational number, respectively.
You can easily get both numbers through (*big.Rat).Denom and (*big.Rat).Num.
Then store them in a structure of your own, with exported (upper case) fields:
type CurrencyValue struct {
Denom int64
Num int64
}
Store this with mgo and convert it back to a *big.Rat in your application through big.NewRat
Edit:
Nick Craig-Wood in the comments correctly noted that big.Rat actually consists of 2 big.Int values, not int values as I had written (easy to miss the upper case i). It's hard to represent a big.Int in BSON but, int64 should cover most use-cases.

Resources