Home > Blockchain >  How to reduce code duplication in function which converts basic types to string in Go
How to reduce code duplication in function which converts basic types to string in Go

Time:05-15

I've written a simple function in Go (1.18.1), which takes any (a.k. interface{}) type as input and returns the value as string, in case of a nil pointer, the function returns "nil"; in case the type is not supported, the function returns "?"

// small helper funcion which is usefull when printing (pointer) values
func ToString(T any) string {
    switch v := T.(type) {
    case string:
        return v
    case *string:
        if v == nil {
            return "nil"
        } else {
            return *v
        }

    case int:
        return strconv.FormatInt(int64(v), 10)
    case int8:
        return strconv.FormatInt(int64(v), 10)
    case int16:
        return strconv.FormatInt(int64(v), 10)
    case int32:
        return strconv.FormatInt(int64(v), 10)
    case int64:
        return strconv.FormatInt(v, 10)
    case float32:
        return fmt.Sprintf("%f", v)
    case float64:
        return fmt.Sprintf("%f", v)

    case *int:
        if v == nil {
            return "nil"
        }
        return strconv.FormatInt(int64(*v), 10)
    case *int8:
        if v == nil {
            return "nil"
        }
        return strconv.FormatInt(int64(*v), 10)
    case *int16:
        if v == nil {
            return "nil"
        }
        return strconv.FormatInt(int64(*v), 10)
    case *int32:
        if v == nil {
            return "nil"
        }
        return strconv.FormatInt(int64(*v), 10)
    case *int64:
        if v == nil {
            return "nil"
        }
        return strconv.FormatInt(*v, 10)
    case *float32:
        if v == nil {
            return "nil"
        }
        return fmt.Sprintf("%f", *v)
    case *float64:
        if v == nil {
            return "nil"
        }
        return fmt.Sprintf("%f", *v)

    case *bool:
        if v == nil {
            return "nil"
        }
    case bool:
        if v == true {
            return "true"
        }
        return "false"
    }
    return "?"
}

This does its job perfectly, but looking at the actual algorithm I'm irritated by the amount of code duplication, unfortunately a fallthrough doesn't work in a type switch statement (see Why isn't fallthrough allowed in a type switch?).

Are there any more efficient ways (i.e. with less code duplication), to do the same thing as the above ToString(T any) function?

CodePudding user response:

Shorter code with reflect:

func ToString(x any) string {
    v := reflect.ValueOf(x)
    if v.Kind() == reflect.Ptr {
        if v.IsZero() {
            return "nil"
        }
        v = v.Elem()
    }

    switch v.Kind() {
    case reflect.String:
        return v.String()
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return strconv.FormatInt(v.Int(), 10)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        return strconv.FormatUint(v.Uint(), 10)
    case reflect.Float32, reflect.Float64:
        return strconv.FormatFloat(v.Float(), 'f', -1, v.Type().Bits())
    case reflect.Bool:
        return strconv.FormatBool(v.Bool())
    default:
        return "?"
    }
}
  •  Tags:  
  • go
  • Related