Iterate over a []rune | string passed in generics - go

I am working with generics with this constrained rule:
type LineParser[T []rune | string] struct {
}
And I have this generic method of that struct:
func (it *LineParser[T]) Parser(line T)
Inside of that method I want to iterate the line but I am getting this error:
invalid operation: cannot slice line (variable of type T constrained by []rune|string): T has no core type
any suggestions?

Convert the line value to a []rune value before iterating. This way, every instance of the method will iterate over the same type.
type LineParser[T []rune | string] struct {}
func (it *LineParser[T]) Parser(line T) {
for _, r := range []rune(line) {
// do something with the next rune
_ = r
}
}

Related

How do you cast an array of unknown type to type []any? [duplicate]

This question already has answers here:
Type converting slices of interfaces
(9 answers)
In Go, how do I pass a slice of interface to something that expects slice of a different compatible interface? [duplicate]
(1 answer)
How can I cast from []interface{} to []int? [duplicate]
(1 answer)
Cannot use args (type []string) as type []interface {} [duplicate]
(1 answer)
Convert map[interface {}]interface {} to map[string]string
(3 answers)
Closed 7 months ago.
Assuming only arrays are passed as arguments to the arr parameter, I would like each call of unpackArray() to return the argument casted from its original array type to type []any.
package main
func unpackArray(arr any) []any {
return arr.([]any)
}
func main() {
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
}
However, this code yields error panic: interface conversion: interface {} is []string, not []interface {}. So it is not allowing me to cast an interface whose static type is not type []any to type []any.
So, given I know that arr's static type is some type of array, and without changing the arr parameter's initialization type from any, how could I convert arr to type []any using this function?
(I am encountering the same problem with maps where I cannot cast from an arbitrary map type to type map[any]any, but I am guessing the solution to this issue would be similar to the solution for arrays.)
Go does not have a builtin "cast" like this, but you can write a function to do it.
You may use reflection to convert a slice of any type to []any:
func unpackArray(s any) []any {
v := reflect.ValueOf(s)
r := make([]any, v.Len())
for i := 0; i < v.Len(); i++ {
r[i] = v.Index(i).Interface()
}
return r
}
You can also use generics in Go 1.18 or later:
func unpackArray[S ~[]E, E any](s S) []any {
r := make([]any, len(s))
for i, e := range s {
r[i] = e
}
return r
}
Both versions of these functions work as requested in the question:
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
Notes:
Go does not have "cast" like some other languages. Go has the somewhat related type assertion and conversion features.
The expression arr.([]any) is a type assertion. The expression asserts that the concrete value in the interface arr has type []any. The expression does not do any conversion.
The code in the question uses slices , not arrays as written in the title.
It's not possible to do that directly, because it's not the same thing.
any is the same of interface{} and each interface{} is two-pointers (the first one is the "metadata"/"type-information" and the second one the pointer to the original data).
If you have []uint{60, 42} you have one slice that each element is 8-byte (considering 64bits). So, if you force it to be []any, each element now take 16 bytes, that breaks everything. You can do it using unsafe.
The only way to "cast" is copying the information, so, you can create a new slice of []any and then append each value into that new slice.
One example of copying is:
// You can edit this code!
package main
func unpackArray[T any](arr any) (r []any) {
o := arr.([]T)
r = make([]any, len(o))
for i, v := range o {
r[i] = any(v)
}
return r
}
func main() {
myArr1 := []string{"Hey"}
myArr2 := []int{60}
unpackArray[string](myArr1)
unpackArray[int](myArr2)
}
However, that doesn't make so much sense, since you can use generics in another way.

Cannot use golang generics variable in function call

I am trying to understand generics implementation in go1.18. In my test example, I am storing a series of test cases and attempting to call a function variable. Unfortunately, I am getting an error in the EvalCases function when I try to use the variable tc.input, I receive the following error:
cannot use input (variable of type T constrained by Inputer) as type
string in argument to fn
Why am I receiving that error, and how do I fix it?
import (
"fmt"
"strconv"
)
type BoolCase func(string) bool
type Inputer interface {
int | float64 | ~string
}
type Wanter interface {
Inputer | bool
}
type TestCase[T Inputer, U Wanter] struct {
input T
want U
}
type TestConditions[T Inputer, U Wanter] map[string]TestCase[T, U]
// IsNumeric validates that a string is either a valid int64 or float64
func IsNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
func EvalCases[T Inputer, U Wanter](cases TestConditions[T, U], fn BoolCase) {
for name, tc := range cases {
input := T(tc.input)
want := tc.want
// Error: cannot use input (variable of type T constrained by Inputer) as type string in argument to fn
got := fn(input)
fmt.Printf("name: %-20s | input: %-10v | want: %-10v | got: %v\n", name, input, want, got)
}
}
func main() {
var cases = TestConditions[string, bool]{
"empty": {input: "", want: false},
"integer": {input: "123", want: true},
"float": {input: "123.456", want: true},
}
fn := IsNumeric
EvalCases(cases, fn)
}
Why am I receiving that error
Because fn is a BoolFunc, which is a func(string) bool, thus requires a string as parameter, but input is of type T. Furthermore, by your definition, T satisfies the Inputer constraint, and thus can also assume type int, float64 or any non-string type which has string as its underlying type (~string) none of which implicitly cast to string.
how do I fix it?
You need to change the definition of BoolCase to itself have a generic type parameter for its one parameter. You could constraint it to Inputer, but you could also use any (interface{}).
type BoolCase[T any] func(T) bool
Then make sure to serve this generic type parameter in the signature of function EvalCases:
func EvalCases[T Inputer, U Wanter](cases TestConditions[T, U], fn BoolCase[T])
https://go.dev/play/p/RdjQXJ0WpDh

Why can't i pass this pointer to other function as an argument in go?

I'm just learning GO (with javascript background)
So, i use this excelize package to create excel document using go.
Here's my code:
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
f := excelize.NewFile()
create(f)
}
func create(f *int) {
fmt.Println(&f)
}
It's giving me an error cannot use f (type *excelize.File) as type *int in argument to create
Is there anything wrong with my code ?
I'm pretty bad with this whole pointer things, but i have to learn go in 2 weeks.
I also put my code in this GO playground site just in case someone want to test it.
create() expects a value of type *int, that is, a pointer to int. f in main() is of type *excelize.File, that is, a pointer to excelize.File.
The value you pass to create() must be assignable to the type of the parameter, and *excelize.File is not assignable to *int. Details: Spec: Calls.
For example, a value of type *int is assignable to the param of create(), so this is valid:
var i int
create(&i) // i is of type int, so &i is of type *int
Or this:
j := new(int) // j is of type *int
create(j)
If you want your create() function to accept a value of any type, use the empty interface: interface{}
func create(f interface{}) {
fmt.Println(f)
}
Then passing f from main() (or any other value) will be valid:
f := excelize.NewFile()
create(f)

Anonymous type declaration works differently via "var" vs ":="

While declaring a variable with anonymous type in Go, I'm seeing var v versus v:= syntaxes work differently. Imagine we're declaring an empty anonymous struct type instance and assign it to a variable.
This works:
func main() {
var v struct {}
_ = v
But this does not:
func main() {
t := struct{}
_ = t
}
compiling this gives the following error (https://play.golang.org/p/MgbttbBVmYE):
prog.go:8:7: type struct {} is not an expression
Why is this the case?
var v struct{} gives v type struct{} but doesn't explicitly set a value, so it gets the zero value {}.
t := struct{} isn't a complete expression. You'd need t := struct{}{} to create t with type struct {} and give it the value {}.
In other words, struct{} is a type, but creating t with := needs a value, not just a type on the right side. struct{}{} is how you write the literal form of an anonymous empty struct.

Does type assertion change the value in go?

Go newbie here.
I have a map where the key arguments should be []string.
However, if I try to use the value directly arguments := m["arguments"] it doesn't seem to be the right type. When used later to append to another slice with arguments... I get Cannot use 'arguments' (type interface{}) as type []string.
I fixed this by chaning the assignment to a type check arguments, _ := m["arguments"].([]string). That works, but I'm not sure why. Is type assertion doing conversion as well?
The full example is below:
import (
"github.com/fatih/structs"
"strings"
)
var playbookKeyDict = map[string]string{
"Playbook": "",
"Limit" : "--limit",
"ExtraVars" : "--extra-vars",
}
type Playbook struct {
Playbook string `json:"playbook" xml:"playbook" form:"playbook" query:"playbook"`
Limit string `json:"limit" xml:"limit" form:"limit" query:"limit"`
ExtraVars string `json:"extra-vars" xml:"extra-vars" form:"extra-vars" query:"extra-vars"`
Arguments []string `json:"arguments" xml:"arguments" form:"arguments" query:"arguments"`
Args []string
}
func (p *Playbook) formatArgs() {
// is it worth iterating through directly with reflection instead of using structs import?
// https://stackoverflow.com/questions/21246642/iterate-over-string-fields-in-struct
m := structs.Map(p)
// direct assignment has the wrong type?
// arguments := m["arguments"]
arguments, _ := m["arguments"].([]string)
delete(m, "arguments")
for k, v := range m {
// Ignore non-strings and empty strings
if val, ok := v.(string); ok && val != "" {
key := playbookKeyDict[k]
if key == "" {
p.Args = append(p.Args, val)
} else {
p.Args = append(p.Args, playbookKeyDict[k], val)
}
}
}
p.Args = append(p.Args, arguments...)
}
Type assertion is used to get a value wrapped around using interface.
m := structs.Map(p)
Map(v interface{}){}
Map function is actually taking interface as its argument in the case stated. It is wrapping the type which is []string and its underlying value which is slice. The type can be checked using Relection reflect.TypeOf().
func TypeOf(i interface{}) Type
According to Russ Cox blog on Interfaces
Interface values are represented as a two-word pair giving a pointer
to information about the type stored in the interface and a pointer to
the associated data.
As specified in Golang spec
For an expression x of interface type and a type T, the primary
expression
x.(T)
asserts that x is not nil and that the value stored in x is of type T.
The notation x.(T) is called a type assertion.
For the error part:-
Cannot use 'arguments' (type interface{}) as type []string
We first needs to get the underlying value of type []string from interface using type assertion.

Resources