Skip to content

Conversation

klauspost
Copy link
Collaborator

@klauspost klauspost commented Sep 17, 2025

This adds support for generic types.

Say you have type Foo[T any] struct, you must add a constraint that it can be handled properly. The constraint is added with a built-in type Foo[T any, _ msgp.RTFor[T].

If T supports serialization this will work. Multiple types can be added, but each must have the constraint.

Nested of types are supported. For example:

type Foo[T any, P msgp.RTFor[T]] struct {
    X  Bar[T, P] `msg:",allownil"`
}

type Bar[T any, _ msgp.RTFor[T]] struct {...}

Notice how P can be used for a nested generic type.

Limitations

  • Types cannot be primitives since they must support marshalling. It can however be an type Int64 int with generation should still work
  • Since we cannot reliably create instances with arbitrary types there are no tests.
  • The msgp.RTFor[T] type shouldn't be used for types in the struct since new instances cannot be created and can cause a runtime crash.
  • There will be warnings like: warn: generics.go: GenericTest: A: possible non-local identifier: T.
  • Some directives may exhibit unexpected behaviour. Not tested too deeply.

Example

// MyStruct can be instantiated with any type T that supports marshaling.
// T must be the base type and not a pointer to work. 
type MyStruct[T any, P msgp.RTFor[T]] struct {
	// Direct use of T
	A  T
	C  []T
	D  map[string]T

	// T used for another generic.
	E  MyStruct2[T, P, string]
	F  []MyStruct2[T, P, string]
	G  map[string]MyStruct2[T, P, string]

	// Same with pointers
	AP *T
	CP []*T
	DP map[string]*T
	EP *MyStruct2[T, P, string]
	FP []*MyStruct2[T, P, string]
	GP map[string]*MyStruct2[T, P, string]
}

// MyStruct2 can be used inside MyStruct.
type MyStruct2[T any, P msgp.RTFor[T], B any] struct {
	A T
}

This adds support for generic types.

Say you have `type Foo[T any] struct`, you must add a constraint that it can be handled properly.
The constraint is added with a built-in `type Foo[T any, _ msgp.RTFor[T]`.

If T supports serialization this will work. Multiple types can be added, but each must have the constraint.

Nested of types are supported. For example:

```go
type Foo[T any, P msgp.RTFor[T]] struct {
    X  Bar[T, P] `msg:",allownil"`
}

type Bar[T any, P msgp.RTFor[T]] struct {...}
```

## Limitations

* Types cannot be primitives since they must support marshalling. It can however be an `type Int64 int` with generation should still work
* Since we cannot reliably create instances with arbitrary types there are no tests.
* The `msgp.RTFor[T]` type shouldn't be used for types in the struct since new instances cannot be created and can cause a runtime crash.
* There will be warnings like: `warn: generics.go: GenericTest: A: possible non-local identifier: T`.
* Some directives may exhibit unexpected behaviour. Not tested too deeply.
@klauspost
Copy link
Collaborator Author

klauspost commented Oct 1, 2025

Ping @philhofer - Any objections to this being merged? Do you see a better approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant