How to pass field as parameter - go

i want to pass field as parameter to return value from function
package main
import (
"fmt"
)
type s struct {
a int
b int
}
func c(s s) int {
var t int
t = s.a // how to change this to t=s.b just by pass parameter
return t
}
func main() {
fmt.Println(c(s{5, 8}))
}
some times i want to make t = s.a and other time i wanted t = s.b to return value 8 the question is how to pass it like parameter
https://play.golang.org/p/JisnrTxF2EY

You may add a 2nd parameter to signal which field you want, for example:
func c2(s s, field int) int {
var t int
switch field {
case 0:
t = s.a
case 1:
t = s.b
}
return t
}
Or a more convenient way is to pass the name of the field, and use reflection to get that field:
func c3(s s, fieldName string) int {
var t int
t = int(reflect.ValueOf(s).FieldByName(fieldName).Int())
return t
}
Or you may pass the address of the field, and assign the pointed value:
func c4(f *int) int {
var t int
t = *f
return t
}
Testing the above solutions:
x := s{5, 8}
fmt.Println("c2 with a:", c2(x, 0))
fmt.Println("c2 with b:", c2(x, 1))
fmt.Println("c3 with a:", c3(x, "a"))
fmt.Println("c3 with b:", c3(x, "b"))
fmt.Println("c4 with a:", c4(&x.a))
fmt.Println("c4 with b:", c4(&x.b))
Which will output (try it on the Go Playground):
c2 with a: 5
c2 with b: 8
c3 with a: 5
c3 with b: 8
c4 with a: 5
c4 with b: 8

Related

How to assign value into reflect Field()?

I faced such a problem.
I need to compare two structure if they type and name of field is equal.
To assign value from sour to dist. I write some code, but here I can assign reflect.Field() value. Could you help me? And I create the test in the bellow
import (
"reflect"
"testing"
)
func Assign(sour interface{}, dist interface{}) uint {
counter := 0
source := reflect.ValueOf(sour)
target := reflect.ValueOf(dist)
typeSource := reflect.TypeOf(sour)
typeTarget := reflect.TypeOf(dist)
for i:=0; i<source.NumField(); i++{
for j:=0; j<target.NumField();j++{
if (typeSource.Field(i).Type==typeTarget.Field(j).Type && typeSource.Field(i).Name==typeTarget.Field(j).Name){
counter = counter + 1
target.FieldByName(typeSource.Field(i).Name).Set(source.Field(i))
}
}
}
return uint(counter)
}
func TestAssign(t *testing.T) {
type A struct {
A string
B uint
C string
}
type B struct {
AA string
B int
C string
}
var (
a = A{
A: "Тест A",
B: 55,
C: "Test C",
}
b = B{
AA: "OKOK",
B: 10,
C: "FAFA",
}
)
result := Assign(a, b)
switch true {
case b.B != 10:
t.Errorf("b.B = %d; need to be 10", b.B)
case b.C != "Test C":
t.Errorf("b.C = %v; need to be 'Test C'", b.C)
case result != 1:
t.Errorf("Assign(a,b) = %d; need to be 1", result)
}
}
For Assign to work, the second argument must be addressable, i.e. you need to pass a pointer to the struct value.
// the second argument MUST be a pointer to the struct
Assing(source, &target)
Then you need to slightly modify your implementation of Assign since a pointer does not have fileds. You can use the Elem() method to get the struct value to which the pointer points.
func Assign(sour interface{}, dist interface{}) uint {
counter := 0
source := reflect.ValueOf(sour)
// dist is expected to be a pointer, so use Elem() to
// get the type of the value to which the pointer points
target := reflect.ValueOf(dist).Elem()
typeSource := reflect.TypeOf(sour)
typeTarget := target.Type()
for i := 0; i < source.NumField(); i++ {
for j := 0; j < target.NumField(); j++ {
if typeSource.Field(i).Type == typeTarget.Field(j).Type && typeSource.Field(i).Name == typeTarget.Field(j).Name {
counter = counter + 1
target.FieldByName(typeSource.Field(i).Name).Set(source.Field(i))
}
}
}
return uint(counter)
}

Go: best practice for safely converting integers to protocol buffer enum values

I have an enum in a proto file that generates to integer constants in the pb.go file. I now have some integers coming from the an external data source and want to safely map them to the possible constants.
Here is what I currently have: https://play.golang.org/p/-5VZqPbukd
package main
import (
"errors"
"fmt"
)
//enum in the proto file
//
// enum X {
// A = 0;
// B = 1;
// C = 2;
// D = 3;
// }
//enum type generated by protoc
type X int32
//enum constants generated by protoc
const (
X_A X = 0
X_B X = 1
X_C X = 2
X_D X = 3
)
func intToX(v int) (X, error) {
x := X(v)
switch x {
case X_A, X_B, X_C, X_D:
return x, nil
}
return 0, errors.New("could not convert int to X")
}
func main() {
for i := -1; i < 10; i++ {
if x, err := intToX(i); err != nil {
fmt.Println("unhandled error:", err, "for input value", i)
} else {
fmt.Printf("%d => X(%d)\n", i, x)
}
}
}
Question: Is there a better, more idiomatic way to map incoming integer values to protoc-generated constants?
In particular, I would like to avoid listing all constants explicitly in the case A, B, C, D statement.
I do not know which proto generation package you are using, but with github.com/golang/protobuf/proto you also get the reverse mapping of enums.
Example xyz.pb.go generated file:
type TimeInterval int32
const (
TimeInterval_TI_UNKNOWN TimeInterval = 0
TimeInterval_TI_HOUR TimeInterval = 1
TimeInterval_TI_DAY TimeInterval = 2
TimeInterval_TI_WEEK TimeInterval = 3
TimeInterval_TI_MONTH TimeInterval = 4
TimeInterval_TI_QUARTER TimeInterval = 5
TimeInterval_TI_YEAR TimeInterval = 6
)
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
var TimeInterval_value = map[string]int32{
"TI_UNKNOWN": 0,
"TI_HOUR": 1,
"TI_DAY": 2,
"TI_WEEK": 3,
"TI_MONTH": 4,
"TI_QUARTER": 5,
"TI_YEAR": 6,
}
func (x TimeInterval) String() string {
return proto.EnumName(TimeInterval_name, int32(x))
}
func (TimeInterval) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
So with this you could test for existence in the following manner:
if _, found := TimeInterval_name[testinputint]; found{
//all ok
} else {
//not a value for this enum
}
Yes as #RickyA mentions using a range is nice as it verifies for all the possible underlying const values.
Additionally you could check the length of the enum, although that would only be possible when the underlying enum values do not have any 'gaps' and have a consequitive range of numbers.
Verbose explanation via code:
typelength := int32(len(TimeInterval_name))
if testinputint < 0 || int32(testinputint) >= typelength {
// not a value for this enum, return err
}
A bit less verbose, and just using int instead of int32
if testinputint < 0 || int(testinputint) >= len(TimeInterval_name) {
// not a value for this enum, return err
}
But as said, this will only be valid for enums that adhere to a proper iota. That might not be the case when you have changed your enum to read something like this:
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
// we do not use month anymore 4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
as the length of the generated map will be clearly less than six :)
In other words use the found method of #Ricky_A above to keep on the safe side.

Slicing using pointer to a slice

I am trying to modify slice a slice in another function, using the following code:
type DT struct {
name string
number int
}
func slicer(a *[]DT) {
tmp := *a
var b []DT
b = append(b, tmp[:1], tmp[2:])
*a = b
}
func main() {
o1 := DT {
name: "name-1",
number: 1,
}
o2 := DT {
name: "name-2",
number: 2,
}
o3 := DT {
name: "name-3",
number: 3,
}
b := make([]DT, 0)
b = append(b, o1)
b = append(b, o2)
b = append(b, o3)
slicer(&b)
fmt.Println(b)
}
What I want is, 1st and last element of the slice. But, in doing so, I am getting following error:
cannot use tmp[:1] (type []DT) as type DT in append
I am relatively new to Go Language, so kindly guide me through this one!
You should use operator ... to convert slice into list of variadic arguments.
b = append(b, tmp[:1]...)
b = append(b, tmp[2:]...)

How to pretty print byte array and char array?

In the following code, I'm trying to write a Txt() function to pretty print out my structure. It contains following minor questions in the full code:
How to write one line to initialize Char array by string(line 47)
How to speed up checking Char type without strings function(line 29,30)
How to print out Char array as string(line 32)
How to print out Char as string, maybe use Sprintf("%c"), but it is very slow.(line 34)
full code at: http://play.golang.org/p/nUsg_qbufP
type Char byte
type THeader struct {
Ver int8 // will show 1
Tag Char // will show 'H'
}
type TBody struct {
B1 [3]byte // will show "[0,0,0]"
B2 [4]Char // will show "ABCD"
}
func Txt(t interface{}) (s string) {
val := reflect.ValueOf(t)
typ := val.Type()
fields := typ.NumField()
for i := 0; i < fields; i++ {
sf := typ.Field(i)
valfld := val.Field(i)
vType := valfld.Type()
s += sf.Name + ":" + vType.String() + ":"
if strings.HasSuffix(vType.String(), "Char") {
if strings.HasPrefix(vType.String(), "[") {
v, ok := valfld.Interface().([4]Char)
s += fmt.Sprint(ok, v) + "\n"
} else {
s += fmt.Sprint(valfld.Interface()) + "\n"
}
} else {
s += fmt.Sprint(valfld.Interface()) + "\n"
}
}
return
}
func main() {
th := THeader{1, 'H'}
fmt.Printf("%#v\n", th)
// tb := TBody{B2: [10]Char("ABCD")}
tb := TBody{B2: [4]Char{'A', 'B', 'C', 'D'}}
fmt.Printf("%#v\n", tb)
fmt.Print("Txt(th):\n", Txt(th), "\n")
fmt.Print("Txt(tb):\n", Txt(tb), "\n")
}
This code should answer all except for your 1'st questions which is impossible since a function can't return arrays of varying length and Go has no facility to initialize an array of dynamically derived sizes. You need slices for those. The rest of the code is solveable using idiomatic go with the Stringer interface and no reflection required.
package main
import (
"fmt"
)
type Char byte
type CharSlice []Char
type ByteSlice []byte
func (s CharSlice) String() string {
ret := "\""
for _, b := range s {
ret += fmt.Sprintf("%c", b)
}
ret += "\""
return ret
}
func (s ByteSlice) String() string {
return fmt.Sprintf("%v", []byte(s))
}
type THeader struct {
Ver int8 // will show 1
Tag Char // will show 'H'
}
func (t THeader) String() string {
return fmt.Sprintf("{ Ver: %d, Tag: %c}", t.Ver, t.Tag)
}
type TBody struct {
B1 [3]byte // will show "[0,0,0]"
B2 [4]Char // will show "ABCD"
}
func (t TBody) String() string {
return fmt.Sprintf("{ B1: %s, B2: %s", ByteSlice(t.B1[:]), CharSlice(t.B2[:]))
}
func main() {
th := THeader{1, 'H'}
fmt.Printf("%#v\n", th)
tb := TBody{B2: [4]Char{'A', 'B', 'C', 'D'}}
fmt.Printf("%#v\n", tb)
fmt.Printf("Txt(th):\n%s\n", th)
fmt.Printf("Txt(tb):\n%s\n", tb)
}

Creating a array of extended types

I have a struct A, extending ("subclassing") it with struct B, like this:
package main
type A struct {
x int
}
type B struct {
A
y int
}
I want to create a array where I can append A or B to it, so that this code works:
func main() {
var m [2]B
m[0] = B { A { 1 }, 2 }
m[0].x = 0
m[0].y = 0
m[1] = A { 3 }
m[1].x = 0
}
It doesn't. If I create the array of the type B, I get "cannot use struct literal (type A) as type B in assignment". If I try to create the array of the type A, I get the the same error (just with the types reversed).
So my question is: which type should the array be?
You could use struct values. For example,
package main
import "fmt"
type A struct {
x int
}
type B struct {
A
y int
}
func main() {
var m []interface{}
m = append(m, B{A{1}, 2})
m = append(m, A{3})
fmt.Println(m[0], m[1])
if b, ok := m[0].(B); ok {
b.x = 0
b.y = 0
m[0] = b
}
if a, ok := m[1].(A); ok {
a.x = 0
m[1] = a
}
fmt.Println(m[0], m[1])
}
Output:
{{1} 2} {3}
{{0} 0} {0}
Or, you could use struct pointers. For example,
package main
import "fmt"
type A struct {
x int
}
type B struct {
A
y int
}
func main() {
var m []interface{}
m = append(m, &B{A{1}, 2})
m = append(m, &A{3})
fmt.Println(m[0], m[1])
if b, ok := m[0].(*B); ok {
b.x = 0
b.y = 0
}
if a, ok := m[1].(*A); ok {
a.x = 0
}
fmt.Println(m[0], m[1])
}
Output:
&{{1} 2} &{3}
&{{0} 0} &{0}
You'll want to define the array type to interface{} rather than B. Then you can store both types in there. That's the only way to accomplish this. If both types implement a specific interface, then you can type to that instead of the generic interface{}

Resources