0

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.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
Romy
  • 329
  • 1
  • 11
  • 4
    What is the _actual_ use case for this? Go's type system is a friend, not an obstacle. – jub0bs Feb 11 '22 at 12:57
  • @BaytaDarell Yeah, I'm able to solve the problem with only. Thanks! @jub0bs I'm writing some unit tests where `input` in a few of the tests can be of type `string` whereas `input` in few of the other tests could be of type `int`, `array` etc. So I wanted to have this common `testCase` struct. – Romy Feb 11 '22 at 15:59
  • 1
    Edit the question to show the code that uses the field. – Charlie Tumahai Feb 11 '22 at 17:21
  • This question might be on topic if you provided more details about how you plan to use that struct. In Go 1.18 you can write such a type, but based on your description I suspect you won't be able to use it in a very productive way... – blackgreen Feb 12 '22 at 07:02

4 Answers4

1

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.

Volker
  • 40,468
  • 7
  • 81
  • 87
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)

}
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.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
-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)
    }
}
Pratheesh M
  • 1,028
  • 6
  • 13