Chris
Chris

Reputation: 17535

Obtaining the name of a known struct field

I have a struct which represents an object in a database, something like:

type Object struct {
    Id string
    Field1 string
    Field2 int
}

And I'd like to have a function that updates the specific field in the database whenever the field is modified, something along these lines:

func (self *Object) SetField1(value string) {
    self.Field1 = value
    database.Update(self.Id, "Field1", self.Field1) // pseudocode
}

Is there a way to replace the "Field1" hard-coded string such that my code is resistant to future changes in the struct field ordering and naming?

I've poked around the reflect package, and it would be nice to be able to get the StructField that represents the field I'm working with, but it seems to require either the name of the field via hard-coded string, or the field's index in the struct (which is subject to change).

Upvotes: 2

Views: 1441

Answers (2)

the system
the system

Reputation: 9336

You could validate your string by adding a method something like this:

func (self *Object) verify(field string) string {
    if _, ok := reflect.TypeOf(*self).FieldByName(field); ok {
        return field
    }
    panic("Invalid field name")
}

And then use it when passing the string to the database update

func (self *Object) SetField1(value string) {
    self.Field1 = value
    database.Update(self.Id, self.verify("Field1"), self.Field1) // pseudocode
}

But I would think that if you're willing to use reflection, that you'd be better off just making a generic setField method that accepts the field as a string, and the value as a interface{}, checks the field and value, sets the value and updates the database.

This way everything is done using the string, so it'll either work or panic, and you don't need to remember to use the .verify() method.

Somthing like:

func (self *Object) SetField(field string, value interface{}) {
    // verify field and value using reflection
    // set the value using reflection
    database.Update(self.Id, field, self.Field1)

}

Though I don't think this'll work on unexported fields.

Upvotes: 3

jorelli
jorelli

Reputation: 8350

not in the context that you're talking about. It's not passed in as a parameter, so you need some other way of specifying which of the struct's fields to be sent. The mental gap here is you're trying to treat that set function like it's a property when it's not; the key difference between a property, as seen in other languages, is that a property is bound to a specific field, whereas your SetField1 method is bound to the whole struct. That method might as well set two fields.

Generally if you're doing field-wise reflective stuff, and you want to do fancy dynamic stuff with fields, you want to use struct tags. e.g., like this:

type Object struct {
    Id string     `db:"id"`
    Field1 string `db:"field1"`
    Field2 int    `db:"field2"`
}

you can access those tags like-a-this:

package main

import (
    "reflect"
    "fmt"
)

func main() {
    var x struct {
        MyField int `core:"required"`
    }
    t := reflect.TypeOf(x)

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Println(field.Name, ":", field.Tag.Get("core"))
    }
}

that's not really a full solution, but... the question in the way it's asked is impossible to do.

Upvotes: 6

Related Questions