Go type definition operation "inheritance"? - go

The Go language specification describes type definition as follows:
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. The new type is called a defined type. It is different from any other type, including the type it is created from.
I have two questions about this description:
What does "operation as the given type" mean, and what is the scope of "operation"(i.e. what counts as operation)? Say I define type A int[] and type B map[string]int, does "same operation" means I can use indexing on variables of type A and key-related operations on variables of type B?
I don't quite understand this description, why is the new type different with its underlying type while keeping the operations? So, the only difference is that they have different methods?

Look at the meaning of "operation as the given type" with context:
"A type definition creates a new, distinct type with the same operation as the given type."
And yes, this means if you could use the index operator on the original type, you can also index the new type. If you could apply the + addition operator on the original type, you can also apply it on the new type. If you could apply the <- receive operator on the original type (e.g. a bidirectional channel), you can also apply on the new type etc.
Basically everything you (may) do with a value is an operation. Applying operators on it, passing it to a function, calling a method on it. Whether an operation is allowed / valid is determined by the value's type, and whether a method call is valid depends on if the method set of the value's type contains the given method.
The new type is different because the type definition creates a new, named type, and Spec: Type identity:
Two types are either identical or different.
A defined type is always different from any other type.
The new type is different by definition. The new type will have zero methods "inherited" from the original type, which comes handy when you don't want the new type implementing certain interfaces, for details and examples, see Inheritance syntax. What is the difference?
You may of course add new methods to your new type. It may be unwanted to add methods to the existing type, or it may be impossible (e.g. because the old type may be a builtin type or may be defined in a package not under your control, and methods can only be added in the defining package).
Type identity (being different) also plays a role in assignability. E.g. a value of unnamed type can be assigned to a variable of named type if the underlying types match, but a value of named type cannot be assigned to a variable of another named type even if the underlying types match (without explicit conversion).
For example:
type Foo []int
type Bar Foo
var i []int = []int{1}
var f Foo = i // OK, []int is unnamed type
var b Bar = f // Not OK, Foo is a named type
Note that the "operations allowed" will have greater significance in the upcoming Go versions, as generics is added to the next (1.18) release, where you use constraints for type parameters, and what types may be used as type arguments for those type parameters is restricted by the operations that can be applied on certain types. For example if we have a simple generic add() function:
func add[T constraints.Ordered | constraints.Complex](a, b T) T {
return a + b
}
We may call it with int, float64, complex64 for example. But if we have our own defined type:
type MyInt int
Since the same operations can be applied on values of MyInt than that of int, we can also use MyInt as a type argument for the above T type parameter:
fmt.Println(add(1, 2)) // add[int]
fmt.Println(add(1+2i, 3+4i)) // add[complex64]
// Yes, below works too, it will be string concatenation
fmt.Println(add("1", "2")) // add[string]
fmt.Println(add(MyInt(1), MyInt(2))) // add[MyInt]
Output will be (try it on the Go Playground):
3
(4+6i)
12
3

The new type keeps the operations from the underlying type. But as it is a different, new type you can then add more operations to it. For example, I've added an "addall" method for type A
package main
import "fmt"
type A []int
type B map[string]int
func (a A) alladd(increment int) A {
for i := 0; i < len(a); i++ {
a[i] = a[i] + increment
}
fmt.Println(a[0])
return a
}
func main() {
var x A
x = append(x, 122)
x[0] = x[0] + 1
var y B = make(B)
y["ABC"] = 999
x = x.alladd(27)
fmt.Println(x, y)
}
https://go.dev/play/p/Od1_-SXk_uO

Related

Get the type name of a generic struct without type parameters

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.

Assignability question in golang specification

While reading go specification "Assignability" section, I tried to execute a couple of examples to get a better understanding of the topic, and now I can't get what am I doing wrong in my code.
According to the specification,
One of the cases when a value x is assignable to a variable of type T is as follows:
x's type V and T have identical underlying types and at least one of V or T is not a defined type.
Defined type specification states that
Type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.
But when I try to run following code, the build fails:
func main() {
type Defined int32
var d Defined
var i int32
d = i
}
The output is:
cannot use i (type int32) as type Defined in assignment
Meanwhile, the similar example with composite literal works fine:
func main() {
type MyMap map[string]int
var x MyMap
var y map[string]int
x = y
}
playground
Also from the spec:
https://golang.org/ref/spec#Numeric_types
To avoid portability issues all numeric types are defined types and thus distinct
Since type Defined int32 defines a new type, d and i don't have identical types; hence, the first clause x's type is identical to T from the assignability spec isn't applicable. The second clause x's type V and T have identical underlying types and at least one of V or T is not a defined type is not applicable as the types of both i and d are defined types. As the remaining clauses from the assignability spec do not apply in this situation, the assignment fails. Changing type Defined int32 to type Defined = int32 (which aliases a type) fixes the error.
x = y due to the T is an interface type and x implements T clause from the assignability spec is applicable.

Go type automatically converting when it seems like it shouldn't

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.

What's the meaning of interface{}?

I'm new to interfaces and trying to do SOAP request by github
I don't understand the meaning of
Msg interface{}
in this code:
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
I've observed the same syntax in
fmt.Println
but don't understand what's being achieved by
interface{}
Note: Go 1.18 (Q1 2022) does rename interface{} to any (alias for interface{}).
See issue 49884, CL 368254 and commit 2580d0e.
See the last part of this answer.
You can refer to the article "How to use interfaces in Go" (based on "Russ Cox’s description of interfaces"):
What is an interface?
An interface is two things:
it is a set of methods,
but it is also a type
The interface{} type (or any with Go 1.18+), the empty interface is the interface that has no methods.
Since there is no implements keyword, all types implement at least zero methods, and satisfying an interface is done automatically, all types satisfy the empty interface.
That means that if you write a function that takes an interface{} value as a parameter, you can supply that function with any value.
(That is what Msg represents in your question: any value)
func DoSomething(v interface{}) {
// ...
}
func DoSomething(v any) {
// ...
}
Here’s where it gets confusing:
inside of the DoSomething function, what is v's type?
Beginner gophers are led to believe that “v is of any type”, but that is wrong.
v is not of any type; it is of interface{} type.
When passing a value into the DoSomething function, the Go runtime will perform a type conversion (if necessary), and convert the value to an interface{} value.
All values have exactly one type at runtime, and v's one static type is interface{} (or any with Go 1.18+).
An interface value is constructed of two words of data:
one word is used to point to a method table for the value’s underlying type,
and the other word is used to point to the actual data being held by that value.
Addendum: This is were Russ's article is quite complete regarding an interface structure:
type Stringer interface {
String() string
}
Interface values are represented as a two-word pair giving a pointer to information about the type stored in the interface and a pointer to the associated data.
Assigning b to an interface value of type Stringer sets both words of the interface value.
The first word in the interface value points at what I call an interface table or itable (pronounced i-table; in the runtime sources, the C implementation name is Itab).
The itable begins with some metadata about the types involved and then becomes a list of function pointers.
Note that the itable corresponds to the interface type, not the dynamic type.
In terms of our example, the itable for Stringer holding type Binary lists the methods used to satisfy Stringer, which is just String: Binary's other methods (Get) make no appearance in the itable.
The second word in the interface value points at the actual data, in this case a copy of b.
The assignment var s Stringer = b makes a copy of b rather than point at b for the same reason that var c uint64 = b makes a copy: if b later changes, s and c are supposed to have the original value, not the new one.
Values stored in interfaces might be arbitrarily large, but only one word is dedicated to holding the value in the interface structure, so the assignment allocates a chunk of memory on the heap and records the pointer in the one-word slot.
Issue 33232 seems to point out to any as an alias to interface{} in Go 1.18 (Q1 2022)
Russ Cox explains:
'any' being only for constraints is a detail that will be in every writeup of generics - books, blog posts, and so on.
If we think we are likely to allow it eventually, it makes sense to allow it from the start and avoid invalidating all that written material.
'any' being only for constraints is an unexpected cut-out that reduces generality and orthogonality of concepts.
It's easy to say "let's just wait and see", but prescribing uses tends to create much more jagged features than full generality. We saw this with type aliases as well (and resisted almost all the proposed cut-outs, thankfully).
If 'any' is allowed in generics but not non-generic code, then it might encourage people to overuse generics simply because 'any' is nicer to write than 'interface{}', when the decision about generics or not should really be made by considering other factors.
If we allow 'any' for ordinary non-generic usage too, then seeing interface{} in code could serve as a kind of signal that the code predates generics and has not yet been reconsidered in the post-generics world.
Some code using interface{} should use generics. Other code should continue to use interfaces.
Rewriting it one way or another to remove the text 'interface{}' would give people a clear way to see what they'd updated and hadn't. (Of course, some code that might be better with generics must still use interface{} for backwards-compatibility reasons, but it can still be updated to confirm that the decision was considered and made.)
That thread also includes an explanation about interface{}:
It's not a special design, but a logical consequence of Go's type declaration syntax.
You can use anonymous interfaces with more than zero methods:
func f(a interface{Foo(); Bar()}) {
a.Foo()
a.Bar()
}
Analogous to how you can use anonymous structs anywhere a type is expected:
func f(a struct{Foo int; Bar string}) {
fmt.Println(a.Foo)
fmt.Println(a.Bar)
}
An empty interface just happens to match all types because all types have at least zero methods.
Removing interface{} would mean removing all interface functionality from the language if you want to stay consistent / don't want to introduce a special case.
interface{} means you can put value of any type, including your own custom type. All types in Go satisfy an empty interface (interface{} is an empty interface).
In your example, Msg field can have value of any type.
Example:
package main
import (
"fmt"
)
type Body struct {
Msg interface{}
}
func main() {
b := Body{}
b.Msg = "5"
fmt.Printf("%#v %T \n", b.Msg, b.Msg) // Output: "5" string
b.Msg = 5
fmt.Printf("%#v %T", b.Msg, b.Msg) //Output: 5 int
}
Go Playground
There are already good answers here. Let me add my own too for others who want to understand it intuitively:
Interface
Here's an interface with one method:
type Runner interface {
Run()
}
So any type that has a Run() method satisfies the Runner interface:
type Program struct {
/* fields */
}
func (p Program) Run() {
/* running */
}
func (p Program) Stop() {
/* stopping */
}
Although the Program type has also a Stop method, it still satisfies the Runner interface because all that is needed is to have all of the methods of an interface to satisfy it.
So, it has a Run method and it satisfies the Runner interface.
Empty Interface
Here's a named empty interface without any methods:
type Empty interface {
/* it has no methods */
}
So any type satisfies this interface. Because, no method is needed to satisfy this interface. For example:
// Because, Empty interface has no methods, following types satisfy the Empty interface
var a Empty
a = 5
a = 6.5
a = "hello"
But, does the Program type above satisfy it? Yes:
a = Program{} // ok
interface{} is equal to the Empty interface above.
var b interface{}
// true: a == b
b = a
b = 9
b = "bye"
As you see, there's nothing mysterious about it but it's very easy to abuse. Stay away from it as much as you can.
https://play.golang.org/p/A-vwTddWJ7G
It's called the empty interface and is implemented by all types, which means you can put anything in the Msg field.
Example :
body := Body{3}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:3}
body = Body{"anything"}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:"anything"}
body = Body{body}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:main.Body{Msg:"anything"}}
This is the logical extension of the fact that a type implements an interface as soon as it has all methods of the interface.
From the Golang Specifications:
An interface type specifies a method set called its interface. A
variable of interface type can store a value of any type with a method
set that is any superset of the interface. Such a type is said to
implement the interface. The value of an uninitialized variable of
interface type is nil.
A type implements any interface comprising any subset of its methods
and may therefore implement several distinct interfaces. For instance,
all types implement the empty interface:
interface{}
The concepts to graps are:
Everything has a Type. You can define a new type, let's call it T. Let's say now our Type T has 3 methods: A, B, C.
The set of methods specified for a type is called the "interface type". Let's call it in our example: T_interface. Is equal to T_interface = (A, B, C)
You can create an "interface type" by defining the signature of the methods. MyInterface = (A, )
When you specify a variable of type, "interface type", you can assign to it only types which have an interface that is a superset of your interface.
That means that all the methods contained in MyInterface have to be contained inside T_interface
You can deduce that all the "interface types" of all the types are a superset of the empty interface.
An example that extends the excellent answer by #VonC and the comment by #NickCraig-Wood. interface{} can point to anything and you need a cast/type assertion to use it.
package main
import (
. "fmt"
"strconv"
)
var c = cat("Fish")
var d = dog("Bone")
func main() {
var i interface{} = c
switch i.(type) {
case cat:
c.Eat() // Fish
}
i = d
switch i.(type) {
case dog:
d.Eat() // Bone
}
i = "4.3"
Printf("%T %v\n", i, i) // string 4.3
s, _ := i.(string) // type assertion
f, _ := strconv.ParseFloat(s, 64)
n := int(f) // type conversion
Printf("%T %v\n", n, n) // int 4
}
type cat string
type dog string
func (c cat) Eat() { Println(c) }
func (d dog) Eat() { Println(d) }
i is a variable of an empty interface with a value cat("Fish"). It is legal to create a method value from a value of interface type. See https://golang.org/ref/spec#Interface_types.
A type switch confirms i interface type is cat("Fish") . See https://golang.org/doc/effective_go.html#type_switch. i is then reassigned to dog("Bone"). A type switch confirms that i interface’s type has changed to dog("Bone") .
You can also ask the compiler to check that the type T implements the interface I by attempting an assignment: var _ I = T{}. See https://golang.org/doc/faq#guarantee_satisfies_interface and https://stackoverflow.com/a/60663003/12817546.
All types implement the empty interface interface{}. See https://talks.golang.org/2012/goforc.slide#44 and https://golang.org/ref/spec#Interface_types . In this example, i is reassigned, this time to a string "4.3".i is then assigned to a new string variable s with i.(string) before s is converted to a float64 type f using strconv. Finally f is converted to n an int type equal to 4. See What is the difference between type conversion and type assertion?
Go's built-in maps and slices, plus the ability to use the empty interface to construct containers (with explicit unboxing) mean in many cases it is possible to write code that does what generics would enable, if less smoothly. See https://golang.org/doc/faq#generics.
Interface is a type which is unknown at compile time
It is a contract between object and the struct type to satisfy with common functionality
or
common functionality acting on different types of struct objects
for example in the below code PrintDetails is a common functionality acting on different types of structs as Engineer,Manager,
Seniorhead
please find the example code
interface examplehttps://play.golang.org/p/QnAqEYGiiF7
A method can bind to any type(int, string, pointer, and so on) in GO
Interface is a way of declear what method one type should have, as long as A type has implement those methods, this can be assigned to this interface.
Interface{} just has no declear of method, so it can accept any type

What is the difference between type conversion and type assertion?

What is the main differences between :
v = t.(aType) // type assertion
v = aType(t) // type conversion
Where should I use type assertion or type conversion ?
A type assertion asserts that t (an interface type) actually is a aType and t will be an aType; namely the one wrapped in the t interface. E.g. if you know that your var reader io.Reader actually is a *bytes.Buffer you can do var br *bytes.Buffer = reader.(*bytes.Buffer).
A type conversion converts one (non-interface) type to another, e.g. a var x uint8 to and int64 like var id int64 = int64(x).
Rule of thumb: If you have to wrap your concrete type into an interface and want your concrete type back, use a type assertion (or type switch). If you need to convert one concrete type to an other, use a type conversion.
tl;dr x.(T) asserts that the dynamic value of interface x is T at run time; T(x) converts the type of an expression x to some other type.
Type Assertion
You know that in Go an interface is basically a method set specification, and you can assign to an interface variable any value whose type implements that method set1.
The type assertion written x.(T) asserts that the value stored in the interface x is of type T. You use a type assertion when you want to unbox that value.
One of the most common uses is when you have interface{} and you need to retrieve the concrete value it stores. A typical example, Context values:
func foo(ctx context.Context) {
s := ctx.Value("my_key").(string) // signature is `Value(key interface{}) interface{}`
// do something with s...
}
It is called assertion because at compile time it is not known whether x actually holds the concrete type T, but you assert that it does. That's why the unchecked assertion y := x.(T) panics if x doesn't actually hold a T — you must use the comma-ok assignment v, ok := x.(T) to avoid it.
ctx = context.WithValue(ctx, "my_key", "foo")
s := ctx.Value("my_key").(int) // panic
v, ok := ctx.Value("my_key").(string)
fmt.Println(v, ok) // "foo" true
In addition, when T in x.(T) is an interface itself, the assertion checks that the value stored in x implements T. The outcome is the same as above.
Type Conversion
A type conversion written as T(x) instead "changes the type of an expression to the type specified by the conversion", i.e. changes the type of x to T. An important property of conversions is that they are statically checked2. An invalid conversion simply won't compile:
type Foo string
type Bar int
a := "foo"
fmt.Println(Bar(a)) // cannot convert a (type string) to type Bar
The main condition for a conversion to be valid is assignability between the types involved, but there's several more, including conversions between numerical types, strings and byte/rune slices, directed channels, slices and array pointers, etc.
In simple terms, you use a conversion when you already know what are the types involved, and simply want to change one to the other:
b := []byte("foo") // converts string literal to byte slice
Notes:
1: more formally, when the value's method set is a superset of the interface method set; this is also why the empty interface interface{} can hold any value, because any set is a superset of an empty set.
2: type assertions are also checked at compile time when the type T in x.(T) does not implement the interface. In practice, this won't help you catch errors when x is interface{} since all types implement it.

Resources