1

The following code attempt to implement a generic CSV writer for any simple struct. By "simple", I mean field value of the struct are of standard, simple types (int, string etc).

type (
    CSV interface {
        Header() []string
        String([]string) (string, error)
    }
    CSVArray []CSV
)


func CSVOutput(w io.Writer, data CSVArray, cols []string) error {
    if len(data) == 0 {
        return nil
    }
    _, err := fmt.Fprintln(w, data[0].Header())
    if err != nil {
        return err
    }
    for _, d := range data {
        str, err := d.String(cols)
        if err != nil {
            return err
        }
        _, err = fmt.Fprintln(w, str)
        if err != nil {
            return err
        }
    }
    return nil
}

The problem is CSVOutput() does not actually work. e.g.:

var data []Employee //the Employee struct implements CSV interface
CSVOutput(w, data, nil)

Compilation failed: cannot use data (type []Employee) as type CSVArray in argument to CSVOutput

I understand that []CSV is not same as []Employee, as explained here, and many other resources available online.

That said, is it possible to rewrite the CSVOutput() function by using reflection:

func CSVOutput(w io.Writer, data interfac{}, cols []string) error {
    sliceOfIntf = castToSlice(data) //how to do this?
    if !implementedCSV(sliceOfIntf[0]) { //and how to do this?
        return errors.New("not csv")
    }
    ... ...
}
1

1 Answer 1

2

is it possible to rewrite the CSVOutput() function by using reflection

Yes

// if data is []Employee{...}, then you can do the following:

rv := reflect.ValueOf(data)
if rv.Kind() != reflect.Slice {
    return fmt.Errorf("data is not slice")
}
if !rv.Type().Elem().Implements(reflect.TypeOf((*CSV)(nil)).Elem()) {
    return fmt.Errorf("slice element does not implement CSV")
}

csvArr := make(CSVArray, rv.Len())
for i := 0; i < rv.Len(); i++ {
    csvArr[i] = rv.Index(i).Interface().(CSV)
}

// now csvArr is CSVArray containing all the elements of data

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

Sign up to request clarification or add additional context in comments.

3 Comments

could you explain what is (*CSV)(nil)? Cast nil into a CSV interface? Can we make it CSV(nil) ?
"what is (*CSV)(nil)" -- that's an expression that converts nil to *CSV. "Can we make it CSV(nil)" -- Nope. You need pointer-to-interface because otherwise the interface-type information would be lost.
@xrfang for a more complete explanation see: stackoverflow.com/a/63938859/965900

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.