如何确保自定义数据结构中的编译时安全

I am writing some data structures to get my feet wet and learn about the Go language and am struggling with Go's lack of generics.

In my implementations I have chosen to force each user to implement an interface so my structures could refer to these objects abstractly but I don't love my solution because this is not verified at compile-time as you will see.

Comparer Interface

Each object that is held in a container must implement a Compare function of the following signature (onerous if all you wanted to hold were raw types)

type Comparer interface {
    Compare(Comparer) int
}

You could then have various elements that implement the interface like float64 or a custom struct:

float64

type number float64

func (n1 number) Compare(comparer Comparer) int {
    n2, _ := comparer.(number)
    if n1 > n2 {
        return 1
    } else if n1 < n2 {
        return -1
    } else {
        return 0
    }
}

Person

type Person struct {
    Age int
}

func (p1 Person) Compare(comparer Comparer) int {
    p2, _ := comparer.(Person)
    if p1.Age > p2.Age {
        return 1
    } else if p1.Age < p2.Age {
        return -1
    } else {
        return 0
    }
}

And now I can compare some of these things:

func main() {
    fmt.Println(number(2).Compare(number(4)))    // -1
    fmt.Println(Person{26}.Compare(Person{28}))  // -1
    fmt.Println(Person{26}.Compare(number(28)))  //  1
}

The problem here is that I should not be able to compare a Person and a number. I realize that I can check the type at runtime but I would like to find either a) a compile-time way to verify the type or b) a different method to implement data structures generically.

Questions

  1. I know that one can do almost everything one might need with the built in data structures ... but how would someone make their own data structures without generics or runtime type checking?
  2. Since interface implementation in Go appears to use duck typing, how does Go enforce types at compile time?

I mean there's nothing unsafe about that code... There just isn't compile time safety. For example, in your method below, the first line does a type assertion on comparer, if it's not a number and you didn't have _ for the second item on the LHS then it would return an error and you could act accordingly. Or you could call it without that at all and a panic will occur leaving it up to the caller to handle it (would be appropriate since they're the person calling the method with wrong arguments, would be like getting an InvalidOperationException in C#).

func (n1 number) Compare(comparer Comparer) int {
    n2, _ := comparer.(number)
    if n1 > n2 {
        return 1
    } else if n1 < n2 {
        return -1
    } else {
        return 0
    }
}

The difference between this and a language like C# is purely in generics, which allow you to do these kinds of things with more compile time safety (because you're not able to call the method incorrectly). That being said, there was a time before C# had generics and many languages before that which didn't feature them at all. These operations are no more unsafe than the casts you do routinely even in languages that do have generics.