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

huangapple go评论90阅读模式
英文:

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

问题

我有以下的结构体:

type testCase struct {
   input   string
   isValid bool
}

我想在多个测试中使用这个结构体,输入可以是stringint等类型。我可以将int类型的输入转换为string,在处理时再将其转换回int,或者我可以定义两个不同的结构体,比如testCaseInttestCaseStruct,这样可以解决我的问题。但是,如何通过将input转换为interface来解决这个问题呢?

我是Go的新手,尝试在Google上搜索相关信息,但可能是因为我不知道该搜索什么,所以没有找到相关结果。

英文:

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.

答案1

得分: 1

在Go语言中,目前(截至Go 1.17版本)无法声明和使用既能存储字符串又能存储整数值的变量。Go的类型系统不支持和类型。

你需要等待Go 1.18版本的发布。

英文:

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

答案2

得分: 0

只有你可以做的是这样,用interface{}改变字符串

在play上检查(它正常工作)

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)

}
英文:

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 &quot;fmt&quot;

type testCase struct {
	input   interface{}
	isValid bool
}

func main() {

	test1 := testCase{}
	test1.input = &quot;STRING&quot;.  // &lt;-------------------STRING
	fmt.Printf(&quot;input: %v \n&quot;, test1)

	test2 := testCase{}
	test2.input = 1      // &lt;-------------------INT
	fmt.Printf(&quot;input: %v \n&quot;, test2)

}

答案3

得分: 0

tl;dr 静态类型和灵活容器之间存在权衡。

在 Go 1.17 及之前的版本中,你不能在结构体字段中使用不同的静态类型。你只能使用 interface{},然后在使用时断言动态类型。这实际上允许你在运行时拥有包含不同类型的 testCases 容器。

在 Go 1.18 中,你可以稍微提高类型安全性,但会牺牲一些灵活性。

  1. 使用联合类型参数化结构体。这样可以在静态上限制允许的类型,但结构体必须显式实例化,因此你不能拥有包含不同实例化的容器。这可能与你的目标是否兼容。
type testCase[T int | string] struct {
   input   T
   isValid bool
}

func main() {
    // 必须使用具体类型进行实例化
    cases := []testCase[int]{
        {500, false},     // 可行,字段接受 int 值
        /*{"foobar", true}, // 不可行,"foobar" 无法赋值给 int*/
    }
    // cases 是一个具有 int 字段的 testCase 切片
}

不,使用 testCase[any] 这样的实例化是一个误导。首先,any 并不满足 int | string 的约束;即使你放宽了约束,它实际上比 Go 1.17 的解决方案更糟糕,因为现在你必须在函数参数中使用 testCase[any] 而不是仅仅使用 testCase

  1. 使用联合类型参数化结构体,但仍然使用 interface{}/any 作为字段类型:如何在 Go 中实现泛型的 Either 类型。这也不允许同时拥有两种类型的容器。

总的来说,如果你的目标是拥有灵活的容器类型(切片、映射、通道),可以接受任意类型,你必须将字段保持为 interface{}/any 并在使用时进行断言。如果你只想在编译时重用具有静态类型的代码,并且你使用的是 Go 1.18,可以使用联合约束。

英文:

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

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}, {&quot;foobar&quot;, true}}

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

<hr>

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
        /*{&quot;foobar&quot;, true}*/, // not ok, &quot;foobar&quot; 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: https://stackoverflow.com/questions/71810399/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.

答案4

得分: -1

方法1:

package main

import (
	"fmt"
)

func main() {
	var a interface{}
	a = "hi"
	if valString, ok := a.(string); ok {
		fmt.Printf("字符串: %s", valString)
	}
	a = 1
	if valInt, ok := a.(int); ok {
		fmt.Printf("\n整数: %d", valInt)
	}
}

方法2:

package main

import (
	"fmt"
)

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

func print(a interface{}) {
	switch t := a.(type) {
	case int:
		fmt.Printf("整数: %v\n", t)
	case string:
		fmt.Printf("字符串: %v\n", t)
	}
}
英文:

Method 1:

package main

import (
	&quot;fmt&quot;
)

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

Method 2:

package main

import (
	&quot;fmt&quot;
)

func main() {
	print(&quot;hi&quot;)
	print(1)
}

func print(a interface{}) {
	switch t := a.(type) {
	case int:
		fmt.Printf(&quot;Integer: %v\n&quot;, t)
	case string:
		fmt.Printf(&quot;String: %v\n&quot;, t)
	}
}

huangapple
  • 本文由 发表于 2022年2月11日 20:43:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/71080287.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定