Type definition - cannot use basic type as custom type even though basic type is source of custom type

Viewed 110

In the below code, we are using type definition syntax in Line 5,6,7:

package math

// Constructors and Selectors

type RationalNumber []int                      // Line 5
type Numerator int
type Denominator int

// Constructor - Construct a rational number x that represents n/d
func NewRationalNumber(n int, d int) RationalNumber {
    g := gcd(n, d) 
    return []int{n / g, d / g}             // Line 12
}

//Selector
func numer(x RationalNumber) Numerator {
    return x[0]                           // Line 17
}

//Selector
func denom(x RationalNumber) Denominator {
    return x[1]
}

return []int{n / g, d / g} does not give error,

where as,

return x[0] & return x[1] give error:

math/numbers.go:17:10: cannot use x[0] (type int) as type Numerator in return argument
math/numbers.go:22:10: cannot use x[1] (type int) as type Denominator in return argument

For given 3 type definitions in Line 5,6,7, I understand the reason behind error on Line 17, but,

  1. Why Line 12 does not give similar error?

  2. How to resolve this error? without changing the signature of functions?

3 Answers

The defined type RationalNumber was created from a slice with elements of type int, therefore subscription on an instance of the defined type gives an int, not any of the newly defined types created from int.

The compiler does not complain about line 12 since the syntax is the slice literal appropriate for creating the defined type (hint: elements of new slice type are int).

You are returning a type different from what was specified in the function definitions.

Take numer(x RationalNumber) Numerator for example

numer(x RationalNumber) Numerator accepts a variable of type RationalNumber and is expected to return a variable of type Numerator. The problem is that you are returning a type int (as RationalNumber is a slice of ints) but a type Numerator is expected.

You can refactor your code to cast the int returned by RationalNumber to the appropriate types.

type RationalNumber []int                    
type Numerator int
type Denominator int

func NewRationalNumber(n int, d int) RationalNumber {
    g := gcd(n, d) 
    return []int{n / g, d / g}  
}

func numer(x RationalNumber) Numerator {
    return Numerator(x[0])                     
}

func denom(x RationalNumber) Denominator {
    return Denominator(x[1])
}

I know that, my question is why there is no necessity to do RationalNumber([]int{n / g, d / g})?

You do not need to do so because []int{n/g, d/g} is already a slice of ints.

type Numerator = int syntax is resolving the error. Am not sure, how type Numerator = int different from type Numerator int? No need to Numerator(x[0])

type Numerator = int is a type alias for int. This does not create a new type that is distinct from int.

Whereas type Numerator int creates a new custom type called Numerator which has a source type of int. Numerator can be cast to int but it is distinct from int

You can refer to Type definitions vs Type Alias for more explanations

This question bothered me as well, so here is answer that i came up with.

Take your function as an example:

func NewRationalNumber(n int, d int) RationalNumber {
    g := gcd(n, d) 
    return []int{n / g, d / g}
}

According to function signature, it should return RationalNumber type, but in fact it returns int slice. Reason why it works is that type []int is assignable to type RationalNumber. Specifically it satisfies second condition, mentioned in language spec:

x's type V and T have identical underlying types and at least one of V or T is not a defined type.

This condition is satisfied, because in that case, we have only one defined type(RationalNumber) and another one is composite type([]int).

So, why do we get error when we define following function?:

func numer(x RationalNumber) Numerator {
    return x[0]
}

Type Numerator has int as underlying type and we return int, which is predeclared type, so it should work, right? No) Thing is, although int is predeclared, it's still defined type. As it's stated in language spec:

To avoid portability issues all numeric types are defined types

In fact, all predeclared types, except error, are defined types.

Related