How to declare and use a struct field which can store both string and int values?
Asked Answered
M

4

1

I've the following struct:

type testCase struct {
   input   string
   isValid bool
}

I want to use this struct in multiple tests and input could be either a string or an intetc.
I can convert the int input to string and convert it back to int while processing, or I can define two different structs e.g. testCaseInt and testCaseStruct which will solve my problem but how do I solve this by converting input to an interface?

I'm new to Go and tried Googling about this but couldn't find maybe because I don't know what to search for.

Macao answered 11/2, 2022 at 12:43 Comment(0)
A
2

How to declare and use a variable which can store both string and int values in Go?

You cannot. Go's type system (as of Go 1.17) doesn't provide sum types.

You will have to wait for Go 1.18.

Apiculture answered 11/2, 2022 at 12:54 Comment(0)
M
1

Method 1:

package main

import (
    "fmt"
)

func main() {
    var a interface{}
    a = "hi"
    if valString, ok := a.(string); ok {
        fmt.Printf("String: %s", valString)
    }
    a = 1
    if valInt, ok := a.(int); ok {
        fmt.Printf("\nInteger: %d", valInt)
    }
}

Method 2:

package main

import (
    "fmt"
)

func main() {
    print("hi")
    print(1)
}

func print(a interface{}) {
    switch t := a.(type) {
    case int:
        fmt.Printf("Integer: %v\n", t)
    case string:
        fmt.Printf("String: %v\n", t)
    }
}
Mihe answered 11/2, 2022 at 12:58 Comment(0)
A
0

only you can do is this, change string with interface{}

check on play (it works fine)

https://go.dev/play/p/pwSZiZp5oVx

package main

import "fmt"

type testCase struct {
    input   interface{}
    isValid bool
}

func main() {

    test1 := testCase{}
    test1.input = "STRING".  // <-------------------STRING
    fmt.Printf("input: %v \n", test1)

    test2 := testCase{}
    test2.input = 1      // <-------------------INT
    fmt.Printf("input: %v \n", test2)

}
Alluvion answered 18/6, 2022 at 1:47 Comment(0)
I
0

tl;dr the trade-off is between static typing and flexible containers.


Up to Go 1.17 you cannot have a struct field with different static types. The best you can have is interface{}, and then assert the dynamic type upon usage. This effectively allows you to have containers of testCases with either type at run time.

type testCase struct {
   input   interface{}
   isValid bool
}

func main() {
    // can initialize container with either type
    cases := []testCase{{500, false}, {"foobar", true}}

    // must type-assert when using
    n := cases[0].(int)
    s := cases[1].(string)
}

With Go 1.18, you can slightly improve on type safety, in exchange for less flexibility.

  1. Parametrize the struct with a union. This statically restricts the allowed types, but the struct now must be instantiated explicitly, so you can't have containers with different instantiations. This may or may not be compatible with your goals.
type testCase[T int | string] struct {
   input   T
   isValid bool
}

func main() {
    // must instantiate with a concrete type
    cases := []testCase[int]{
        {500, false},     // ok, field takes int value
        /*{"foobar", true}*/, // not ok, "foobar" not assignable to int
    }
    // cases is a slice of testCase with int fields
}

No, instantiating as testCase[any] is a red herring. First of all, any just doesn't satisfy the constraint int | string; even if you relax that, it's actually worse than the Go 1.17 solution, because now instead of using just testCase in function arguments, you must use exactly testCase[any].

  1. Parametrize the struct with a union but still use interface{}/any as field type: (How) can I implement a generic `Either` type in go? . This also doesn't allow to have containers with both types.

In general, if your goal is to have flexible container types (slices, maps, chans) with either type, you have to keep the field as interface{}/any and assert on usage. If you just want to reuse code with static typing at compile-time and you are on Go 1.18, use the union constraint.

Improper answered 13/7, 2022 at 6:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.