Home > Blockchain >  How to write a generic function that takes a string or an error as a parameter?
How to write a generic function that takes a string or an error as a parameter?

Time:10-07

Is there any way to take a string or an error as a generic parameter?

package controller

import (
    "fmt"
    "net/http"

    "github.com/gin-gonic/gin"
)

type ServerError[T fmt.Stringer] struct {
    Reason T `json:"reason"`
}

func ResponseWithBadRequest[T fmt.Stringer](c *gin.Context, reason T) {
    c.AbortWithStatusJSON(http.StatusBadRequest, ServerError[T]{Reason: reason})
}

The above code contains a helper function tries to response an http request with a json containing one generic field that I would like it to be a string or an error.

But when I try to input a string:

string does not implement fmt.Stringer (missing method String)

which I find very amusing.

I tried to change T fmt.Stringer to T string | fmt.Stringer:

cannot use fmt.Stringer in union (fmt.Stringer contains methods)

I understand the reason is that string in golang is a primitive data type without any method, I would like to know if there's a possible way to do this.


Update:

As @nipuna pointed out in the comment, error is not a Stringer either.

CodePudding user response:

Is there any way to take a string or an error as a generic parameter?

No. As stated, the constraint you are looking for is ~string | error, which doesn't work because interfaces with methods can't be used in unions.

And error is indeed an interface with the Error() string method.

The sensible way to handle this is to drop generics and define Reason as a string:

type ServerError struct {
    Reason string `json:"reason"`
}

You can find more details about that here: Golang Error Types are empty when encoded to JSON. The tl;dr is that error can't shouldn't be encoded to JSON directly; you'll eventually have to extract its string message anyway.

So in the end you're going to do something like this with strings:

reason := "something was wrong"
c.AbortWithStatusJSON(http.StatusBadRequest, ServerError{reason})

and something like this with errors:

reason := errors.New("something was wrong")
c.AbortWithStatusJSON(http.StatusBadRequest, ServerError{reason.Error()})
  • Related