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