gotypesstandard-library

What's the difference between types.Implements and types.Satisfies?


What's the difference between types.Implements and types.Satisfies in golang?

I'm working on a code that inspects Go AST.


Solution

  • tl;dr

    A type may "satisfy" comparable without "implementing" it, i.e. without belonging to its type set.


    The method names are directly related to the language specification, so it helps to look at that first:

    Implementing is defined as:

    A type T implements an interface I if

    • T is not an interface and is an element of the type set of I; or
    • T is an interface and the type set of T is a subset of the type set of I.

    The concept of "type set" is also defined in the specs and depends on what kind of interface you are considering. Basic interfaces are those which include only methods, and the type set is the set of all types that implement those methods, e.g.:

    // type set includes all types with the method Read (signature must match)
    type Reader interface {
        Read([]byte) (int, error)
    } 
    

    Non-basic interfaces are those which include type terms (and zero or more methods), the type set is the set of the specified types. If any term is approximate, e.g. ~int the set includes types that have int as underlying type.

    // type set includes int, string and anything with int or string as underlying type
    type Foo interface {
        ~int | ~string
    }
    

    Satisfying is used only for interface constraints and is defined as:

    A type T satisfies a constraint C if

    • T implements C; or
    • C can be written in the form interface{ comparable; E }, where E is a basic interface and T is comparable and implements E.

    As you can see the definition of "satisfying" includes "implementing" plus an exception.

    Before Go 1.20, the concept of "satisfying" an interface — any interface — didn't exist. Every use case was called "implementing".

    In Go 1.20 "satisfying" was introduced to expand the definition of "implementing a constraint" with an exception (the second bullet point in the above quote) for types that had to satisfy comparable. Before, types such as reflect.Type or [2]io.Reader didn't implement comparable despite supporting == and != operators in non-generic code.

    In other words, there was a mismatch between the definition of comparable as per the spec, and the actual type set of the comparable constraint.

    More info: Go generics: type constraint for map keys?