I don't think there is such support in Go generics as of now. I may be 
wrong though. 

The alternative is to use reflection. Here is the simplified code of your 
example:

```go
func GetValidator(field Field) Validator {
        return Validator{
                Validate: func() (int, error) {
                        value := reflect.ValueOf(field.Value)
                        if value.Kind() != reflect.Map && value.Kind() != 
reflect.Slice && value.Kind() != reflect.Array {
                                return 0, errors.New("invalid type")
                        }
                        return value.Len(), nil
                },
        }
}
```

On Friday, March 18, 2022 at 5:47:29 PM UTC+7 RussellLuo wrote:

> Thanks for your reply, Henry!
>
> I admit that my problem seems a little bit silly without providing 
> the contextual information. Actually I am trying to rewrite my little 
> [validation library](https://github.com/RussellLuo/validating) by 
> leveraging Go generics.
>
> For the originally non-generic [Len](
> https://pkg.go.dev/github.com/RussellLuo/validating/v2#Len) validator 
> factory, I have implemented two generic versions. One for a slice:
>
> ```go
> // LenSlice is a leaf validator factory used to create a validator, which 
> will
> // succeed when the length of the slice field is between min and max.
> func LenSlice[T ~[]E, E any](min, max int) (mv *MessageValidator) {
>         mv = &MessageValidator{
>                 Message: "with an invalid length",
>                 Validator: Func(func(field *Field) Errors {
>                         v, ok := field.Value.(T)
>                         if !ok {
>                                 return NewUnsupportedErrors(field, 
> "LenSlice")
>                         }
>
>                         l := len(v)
>                         if l < min && l > max {
>                                 return NewErrors(field.Name, ErrInvalid, 
> mv.Message)
>                         }
>                         return nil
>                 }),
>         }
>         return
> }
> ```
>
> and the other for a map:
>
> ```go
> // LenMap is a leaf validator factory used to create a validator, which 
> will
> // succeed when the length of the map field is between min and max.
> func LenMap[T map[K]V, K comparable, V any](min, max int) (mv 
> *MessageValidator) {
>         mv = &MessageValidator{
>                 Message: "with an invalid length",
>                 Validator: Func(func(field *Field) Errors {
>                         v, ok := field.Value.(T)
>                         if !ok {
>                                 return NewUnsupportedErrors(field, 
> "LenMap")
>                         }
>
>                         l := len(v)
>                         if l < min && l > max {
>                                 return NewErrors(field.Name, ErrInvalid, 
> mv.Message)
>                         }
>                         return nil
>                 }),
>         }
>         return
> }
> ```
>
> As a result, we can use them as below:
>
> ```go
> Schema{
>         F("slice", []string{"foo"}): LenSlice[[]string](0, 2),
>         F("map", map[string]int{"a": 1, "b": 2}): 
> LenMap[map[string]int](0, 2),
> }
> ```
>
> *Now I am wondering if I can merge the above two versions into one generic 
> `Len`, with the help of a constraint, say, SliceOrMap*. For example:
>
> ```go
> func Len[T SliceOrMap](min, max int) (mv *MessageValidator) {
>         mv = &MessageValidator{
>                 Message: "with an invalid length",
>                 Validator: Func(func(field *Field) Errors {
>                         v, ok := field.Value.(T)
>                         if !ok {
>                                 return NewUnsupportedErrors(field, "Len")
>                         }
>
>                         l := len(v)
>                         if l < min && l > max {
>                                 return NewErrors(field.Name, ErrInvalid, 
> mv.Message)
>                         }
>                         return nil
>                 }),
>         }
>         return
> }
> ```
>
> Then we can use the same `Len` for both a slice and a map as below instead:
>
> ```go
> Schema{
>         F("slice", []string{"foo"}): Len[[]string](0, 2),
>         F("map", map[string]int{"a": 1, "b": 2}): Len[map[string]int](0, 
> 2),
> }
> ```
>
> *(NOTE*: I have posted this reply earlier, but is was not displayed 
> (maybe due to my wrong choice of "Reply to author").  Now I post it again 
> by selecting "Reply all".)
> On Friday, March 18, 2022 at 3:07:17 PM UTC+8 Henry wrote:
>
>> Have you considered this?
>> ```go
>> func IsBetween(value, min, max int) bool {
>>    return value>=min && value <=max
>> }
>>
>> if IsBetween(len(myMap), 10, 25) {
>>   //do something
>> }
>> ```
>>
>> On Friday, March 18, 2022 at 11:20:37 AM UTC+7 RussellLuo wrote:
>>
>>> Hi there,
>>>
>>> Thanks to Go generics in 1.18, I can write a generic function 
>>> `LenBetween` for a slice:
>>>
>>> ```go
>>> func SliceLenBetween[T ~[]E, E any](s T, min, max int) bool {
>>>         return len(s) >= min && len(s) <= max
>>> }
>>> ```
>>>
>>> as well as for a map:
>>>
>>> ```go
>>> func MapLenBetween[T map[K]V, K comparable, V any](s T, min, max int) 
>>> bool {
>>>         return len(s) >= min && len(s) <= max
>>> }
>>> ```
>>>
>>> Is there any way to write a constraint, say, SliceOrMap, to support 
>>> either a slice or a map?
>>>
>>> With the help of SliceOrMap, then I can write a more generic version 
>>> `LenBetween` like this:
>>>
>>> ```go
>>> func MapLenBetween[T SliceOrMap](s T, min, max int) bool {
>>>         return len(s) >= min && len(s) <= max
>>> }
>>> ```
>>>
>>> Thanks in advance!
>>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/3ee74f75-9c07-4334-a149-25594560cacbn%40googlegroups.com.

Reply via email to