There is a piece of GO code I have seen in the GIN library and in the Google docs that look like the following
type (
T0 []string
T1 []string
T2 struct{ a, b int }
T3 struct{ a, c int }
T4 func(int, float64) *T0
T5 func(x int, y float64) *[]string
)
What I don't understand is, what this grouping is doing and what are some purposes of this implementation (there wasn't much in the docs that went over this topic unless I missed it)
another example from the gin library
type (
RoutesInfo []RouteInfo
RouteInfo struct {
Method string
Path string
Handler string
}
Engine struct {
RouterGroup
HTMLRender render.HTMLRender
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
trees methodTrees
RedirectTrailingSlash bool
RedirectFixedPath bool
HandleMethodNotAllowed bool
ForwardedByClientIP bool
}
)
And lastly - sorry this is different topic but related to this one
var _ IRouter = &Engine{}
why is there a _ infront of IRouter? I know it's a blank identifier but what purpose does it have in that case
The code
type (
A int
B string
)
is functionality identical to
type A int
type B string
The grouping is just a way to organize code. The grouping is sometimes used to indicate that the types are related in some way.
The use of the blank identifier is explained in Specs: What's the purpose of the blank identifier in variable assignment?.
I think the first part of your question has already been answered. As for the second part, the code
var _ IRouter = &Engine{}
Creates a compile-time check that *Engine implements the IRouter interface. We are assigning a value of type *Engine to a variable that must be of the type IRouter. If a *Engine does not satisfy the interface then it won't compile.
This isn't strictly necessary but some people like to put it in where they define a type to ensure it always satisfies an intended interface.
Regarding 1st question
The statments shown are type declaration
From the Go language spec :
A type declaration binds an identifier, the type name, to a new type that has the same underlying type as an existing type, and operations defined for the existing type are also defined for the new type. The new type is different from the existing type.
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = identifier Type .
In layman terms, you can do :
type
e.g.
type Temperature uint8
type WeekTemp [7]Temperature
For underlying type,
Named instances of the boolean, numeric, and string types are predeclared.
Composite types are constructed using type literals
TypeLiteral = ArrayType | StructType | PointerType | FunctionType |
InterfaceType |
SliceType | MapType | ChannelType .
so you can also equivalently do :
type (
Temperature unit8
WeekTemp [7]Temperature
)
another example for composite type
type Tree struct {
leftChild, rightChild *Tree
}
For 2nd question,
Edited :
I wasn't aware of this but trying on go playground , it looks like the answer from jcbwlkr is indeed right.
So trying to compile
type MyInterface interface{
someMethod()
}
type MyType struct{}
var _ MyInterface = &MyType{}
will give this error
cannot use MyType literal (type *MyType) as type MyInterface in
assignment: *MyType does not implement MyInterface (missing
someMethod method)
So it indeed does a compile time check to make sure that Engine struct conforms to IRouter interface. neat
Related
Say I have a generic struct called foo and I create two objects from it. I can determine the concrete type of each using reflect.TypeOf(), like so:
package main
import (
"fmt"
"reflect"
)
type foo[T any] struct {
data T
}
func main() {
a := foo[string]{"cheese"}
b := foo[int]{42}
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(b))
}
// main.foo[string]
// main.foo[int]
What I am interested in is determining just the generic type of these objects (i.e., foo) and not the concrete type (i.e., foo[string] and foo[int]). Is this possible or do I need to manually extract the generic type from these strings (e.g., with regex)?
Edit
Regex might look something like this:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
fmt.Println(GetGenericType(a))
fmt.Println(GetGenericType(b))
// foo
// foo
I've also seen this question but this doesn't answer this question because it gives the concrete type (i.e., main.foo[string]) rather than the generic type (i.e., foo).
Reflection doesn't see the name of the "base" generic type, because at run time that base type doesn't exist.
The relevant passage from the Go spec is Instantiations:
Instantiating a type results in a new non-generic named type; instantiating a function produces a new non-generic function.
So when you write:
b := foo[int]{42}
name := reflect.TypeOf(b).Name()
the name of that type is precisely foo[int].
It's worth noting that the identifier foo without the type parameter list is relevant at compile time, because it prevents you from redeclaring it in the same package. Type definitions:
A type definition creates a new, distinct type with the same
underlying type and operations as the given type and binds an
identifier, the type name, to it.
TypeDef = identifier [ TypeParameters ] Type .
But instantiations, as defined above, result in a new named type which is different than foo; and at run time when you can use reflection, you deal with instantiations only.
In conclusion, I think your solution with regex is acceptable, until some helper function is added to the stdlib (if ever). Reposting it here for clarity:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
Just keep in mind the difference between Type.String() and Type.Name(): any type can have a string representation, but only named types have a name. (Obviously, right?). So for example if you wrote:
b := &foo[int]{42}
then the type of b is *foo[int], which is an anonymous composite type, and Name() returns an empty string.
This question already has answers here:
Assigning a value literal to a struct field of a generic type without running into an IncompatibleAssign error
(1 answer)
Convert a type (int, float etc) to `T` [go1.18]
(2 answers)
How to declare and use a struct field which can store both string and int values?
(4 answers)
Closed 4 months ago.
Given a generic struct:
type R2[IDTYPE comparable] struct {
ID IDTYPE
IsActive bool
}
Implementing an interface:
type Storable interface {
Store(ctx context.Context) error
}
I would expect the following definition to work:
func (r R2[int]) Store(ctx context.Context) error {
r.ID = 123 // not allowed
// ...
return nil
}
However, the method definition is not allowed. The error is:
'123' (type untyped int) cannot be represented by the type IDTYPE (int)
Is it not yet possible to do this kind of generic field assignment in Go?
Addendum:
On go playground the error is:
cannot use 123 (untyped int constant) as int value in assignment
And converting to int(123) does not work. The error in this case is:
cannot use comparable(123) (untyped int constant 123) as int value in assignment
Instantiation must happen at the type level, not on a method level, and methods can't introduce new type parameters, see How to create generic method in Go? (method must have no type parameters)
This means when you want to use R2, you then have to choose type arguments for the type parameters, and the methods can't change those, you're "stuck" with the types you choose on R2's instantiation.
Also note that since the constraint for IDTYPE is comparable, which may be string for example, the integer 123 cannot be assigned to the ID field in all cases because it may have a type of string.
If you want / must handle multiple concrete types for the IDs, generics is not the right choice. Interfaces may be used instead:
type R2 struct {
ID any
IsActive bool
}
Also note that the receiver must be a pointer if you wish to modify the receiver (e.g. fields of a struct).
If you wish to restrict the values stored in ID to comparable, use a (generic) function for it.
Here's how you can do it:
type R2 struct {
ID any
IsActive bool
}
func (r *R2) Store(ctx context.Context) error {
setID(r, 123)
return nil
}
func setID[ID comparable](r *R2, id ID) {
r.ID = id
}
Testing it:
r := &R2{}
var s Storable = r
s.Store(context.TODO())
fmt.Println(r)
Which outputs (try it on the Go Playground):
&{123 false}
This provides flexibility (you can set any comparable values to the ID field using setID()), and provides compile-time safety: attempting to set an incomparable value will result in a compile-time error such as this:
setID(r, []int{1}) // Error: []int does not implement comparable
Go introduces the new token ~.
~T means the set of all types with underlying type T
However, I could not understand it, ask someone to help explain.
The following is an example.
type Ordered interface {
Integer | Float | ~string
}
In Go generics, the ~ tilde token is used in the form ~T to denote the set of types whose underlying type is T.
It was also called "approximation" constraint element in the generics proposal, which explains what it's good for in plain language:
Listing a single type is useless by itself. For constraint satisfaction, we want to be able to say not just int, but “any type whose underlying type is int”. [...] If a program uses type MyString string, the program can use the < operator with values of type MyString. It should be possible to instantiate [a function] with the type MyString.
If you want a formal reference, the language spec has placed the definition of underlying types in its own section:
Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its type declaration.
This covers the very common cases of type literals and other composite types with bound identifiers, or types you define over predeclared identifiers, which is the case mentioned in the generics proposal:
// underlying type = struct literal -> itself -> struct { n int }
type Foo struct {
n int
}
// underlying type = slice literal -> itself -> []byte
type ByteSlice []byte
// underlying type = predeclared -> itself -> int8
type MyInt8 int8
// underlying type = predeclared -> itself -> string
type MyString string
The practical implication is that an interface constraint whose type set has only exact elements doesn't allow your own defined types:
// hypothetical constraint without approximation elements
type ExactSigned interface {
int | int8 | int16 | int32 | int64
}
// CANNOT instantiate with MyInt8
func echoExact[T ExactSigned](t T) T { return t }
// constraints.Signed uses approximation elements e.g. ~int8
// CAN instantiate with MyInt8
func echo[T constraints.Signed](t T) T { return t }
As with other constraint elements, you can use the approximation elements in unions, as in constraints.Signed or in anonymous constraints with or without syntactic sugar. Notably the syntactic sugar with only one approx element is valid:
// anonymous constraint
func echoFixedSize[T interface { ~int8 | ~int32 | ~int64 }](t T) T {
return t
}
// anonymous constraint with syntactic sugar
func echoFixedSizeSugar[T ~int8 | ~int32 | ~int64](t T) T {
return t
}
// anonymous constraint with syntactic sugar and one element
func echoFixedSizeSugarOne[T ~int8](t T) T {
return t
}
As anticipated above, a common use case for approximation elements is with composite types (slices, structs, etc.) that need to have methods. In that case you must bind the identifier:
// must bind identifier in order to declare methods
type ByteSeq []byte
func (b ByteSeq) DoSomething() {}
and now the approximation element is handy to allow instantiation with ByteSeq:
// ByteSeq not allowed, or must convert func argument first
func foobar[T interface { []byte }](t T) { /* ... */ }
// ByteSeq allowed
func bazquux[T interface { ~[]byte }](t T) { /* ... */ }
func main() {
b := []byte{0x00, 0x01}
seq := ByteSeq{0x02, 0x03}
foobar(b) // ok
foobar(seq) // compiler error
foobar([]byte(seq)) // ok, allows inference
foobar[[]byte](seq) // ok, explicit instantiation, then can assign seq to argument type []byte
bazquux(b) // ok
bazquux(seq) // ok
}
NOTE: you can not use the approximation token with a type parameter:
// INVALID!
type AnyApprox[T any] interface {
~T
}
There is not just the new token, but the new syntax for interfaces. You can declare an interface with type constraints in addition to method constraints.
To satisfy an interface, the type must satisfy both the method constraints and the type constraints.
From the docs:
An interface representing all types with underlying type int which implement the String method.
interface {
~int
String() string
}
For a type to have an "underlying type" of int, that means the type takes the following form:
type SomeType int
And to satisfy the method constraint, there must be declared a method with the specified signature:
func (v SomeType) String() string {
return fmt.Sprintf("%d", v)
}
This is a Go grammar question and seems a stupid question but I've been checking the Go language specification to find some official words or definitions to define what type is a xxx type, say, what type is an interface type?
For example, I see words like these:
The method set of an interface type is its interface.
Or
An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type.
Or
Consider a struct type T with two methods ...
type T struct {
a int
}
....
A type literal like struct {...} is a struct type, what about A in
type A struct {...} and B in type B interface{...}? Is A a struct type and B an interface type?
Yes, from the above sample for the struct type T, I can tell that a defined type (by the "type" declaration) whose given type is a struct type or interface type is also a struct or interface type. So A is a struct type and B is an interface type also. But where are the official definitions of this rule?
For defined types I can only find the following relating to type categories:
A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.
So my understanding is that the defined type is a new, distinct type with the given type, but they are in the same type category, say, interface types or struct types. Still, there are no such definitions.
TLDR;
The kind of type T is interface if its underlying type is an interface type.
The kind of type T is struct if its underlying type is a struct type.
Spec: Struct types and Spec: Interface types specifies exactly what are the struct and interface types:
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag = string_lit .
InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
MethodSpec = MethodName Signature | InterfaceTypeName .
MethodName = identifier .
InterfaceTypeName = TypeName .
So for example these are struct types:
struct { A int }
struct {}
struct { _ int }
and these are interface types:
interface { String() string }
interface {}
We may use a type declaration to create a new type, such as:
type Point struct { X, Y int }
The above type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it. The definition of underlying type is recursive:
Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its type declaration.
When we talk about arbitrary types being structs or interfaces, we're talking about their kind.
In the light of this, basically your question is equivalent to this:
"When is the kind of an arbitrary type interface or struct?"
The answer to this question is not in the spec, but this is how we could define it:
The kind of a type T is interface if its underlying type is an interface type.
Similarly:
The kind of a type T is struct if its underlying type is a struct type.
So for example:
type Point struct { X, Y int }
type PP Point
Is the type struct { X, Y int } of kind struct? Yes, because since it's a type literal, its underlying type is itself, and it's by definition a struct type.
Is Point a struct? Since the underlying type of Point is the underlying type of the type to which it refers in its type declaration, which is a type literal (see above), it is of struct type (its kind is struct).
Is PP a struct? Since its underlying type is the underlying type of the type to which it refers in its type declaration (which is Point), whose underlying type is a struct type literal, yes, it is also a struct type.
This kind we're talking about is represented by the reflect.Kind type. There are reflect.Interface and reflect.Struct constants (of type reflect.Kind) to represent the struct and interface kinds. And the reflect.Type type descriptor has a Type.Kind() method to access this kind.
This is how you can check if the type (kind) of some value is a struct for example:
func isStruct(i interface{}) bool {
return reflect.TypeOf(i).Kind() == reflect.Struct
}
Testing it (try it on the Go Playground):
fmt.Println(isStruct(Point{})) // true
fmt.Println(isStruct(PP{})) // true
fmt.Println(isStruct(struct{}{})) // true
fmt.Println(isStruct("text")) // false
Checking for interface type is a little more complicated because passing an interface value to a function that expects interface{} will not pass the interface value as-is but the concrete value "stored" in it, and as an interface{} value. We would have to pass a pointer to interface (which otherwise rarely makes sense in Go), access the element type and check its kind. For details, see this answer: What is the difference between reflect.ValueOf() and Value.Elem() in go?
An interface type is a type introduced by the interface keyword, or a name for such a type as defined by type name interface (plus of course the actual requirements for that interface).
Any type, whether it is an interface type or not, implements an interface type if it has the appropriate set of named methods. That is, a struct type may be sufficient to be used with some interface type. Even a non-struct type may be sufficient:
type foo int
func (receiver_arg foo) method1() { ... }
Type-name foo now implements any interface that requires a method named method1 (provided of course that it implements the rest of any required methods).
... the defined type [via the type keyword] is a new, distinct type with the given type, but they are in the same type category ...
Yes, just so. Using reflect, you'll find that they have the same Kind. The word kind isn't in the spec like this but it's quite useful, and the link here enumerates all the fundamental type-kinds in Go.
Sorry for the ambiguous title. I'm not getting a compiler error when I believe that I should, based on creating a new type and a function that takes an argument of that type.
The example:
package search
//Some random type alias
type Search string
//Takes a string and returns Search
func NewSearch(s string) Search {
return Search(s)
}
//This is where things are getting weird
//Returns an int just for arbitrary testing
func PrintSearch(s Search) int{
return 5
}
Now my assumption would be, if I created an object using NewSearch, I would be able to pass it to PrintSearch and have everything run as expected, but if I passed PrintSearch a primitive string, it should not compile. I am not experiencing this behavior.
The main code:
package main
import (
"fmt"
".../search" //no need to type the whole path here
)
func main() {
SearchTerm := search.NewSearch("Test")
StringTerm := "Another test"
fmt.Println(search.PrintSearch(SearchTerm)) // This should print 5
fmt.Println(search.PrintSearch(StringTerm)) // This should throw a compiler error, but it is not
}
It seems like if I write the type and the function in the same package as main, everything works as I'd expect? As in, it throws a compiler error. Is there something I've missed about cross-package type coercion?
We can simplify this example a bit further (playground):
package main
type Foo string
type Bar int
func main() {
var f Foo = "foo"
var b Bar = 1
println(f, b)
}
This is explained in the spec's assignability section.
A value x is assignable to a variable of type T ("x is assignable to
T") in any of these cases:
x's type is identical to T.
x's type V and T have identical underlying types and at least one of V or T is not a named type.
T is an interface type and x implements T.
x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
x is an untyped constant representable by a value of type T.