The content on this page is written in Chinese, and then traslated into English by machine. More accurate traslations are welcome at: https://github.com/wa-lang/man/tree/master/en

Ending's law: "Any application that can be compiled to WebAssembly, will be compiled to WebAssembly eventually."

7.2. Non-null interface

An interface is a collection of methods. The general form of an interface declaration is as follows:

type InterfaceName interface {
    MethodSet
}

The attributes of methods in the MethodSet include the method name and the function signature of the method. For example, we define an interface as follows:

type Stringer interface {
    String() => string
}

The interface is named Stringer and contains a method named String, which has no input parameters and returns a string.

If a method set of a concrete type T is a superset of the method set MethodSet_i of a certain interface I, then we say: Type T satisfies the interface I. In other words, let the method set of type T be St, the method set of interface I be Si, and the necessary and sufficient condition for type T to satisfy interface I is: any m ∈ Si , there exists m' ∈ St, such that m and m' have the same name and the same function signature.

If the type T satisfies the interface I, then the value of type T will be assigned to the interface value of type I. When performing the assignment operation, the value of type T will be Copy to the interface value; this is also the reason why the empty interface is the universal wrapper in the previous section, because according to the above definition, the method set of interface{} is empty, and any type can satisfy it.

Interface methods can be called, and the call will dynamically switch to the method with the same name of the concrete value contained in the interface value (if the concrete value contained in the interface value is nil, then the call will trigger a runtime exception). Non-null interface is an important abstraction method in Wa-lang. Different types of objects can satisfy the same interface, so that callers can use different types of objects in a unified way through the interface. Therefore, the essence of the interface is a set of method conventions, and the check of this convention (whether the concrete type satisfies a certain interface), which is completed at compile time. Here is a example:

// 版权 @2023 凹语言 作者。保留所有权利。

type Printer interface {
    Print()
}

type T1 struct {
    i: i32
}

func T1.Print {
    println("This is T1, this.i:", this.i)
}

type T2 struct {
    s: string
}

func T2.Print {
    println("This is T2, this.s:", this.s)
}

func PrintObj(p: Printer) {
    p.Print()
}

func main {
    p: Printer

    v1: T1
    v1.i = 42
    p = &v1
    PrintObj(p) // This is T1, this.i: 42

    v2: T2
    v2.s = "你好"
    p = &v2
    PrintObj(p) // This is T2, this.s: 你好
}

It can be seen that when different objects are encapsulated in the same interface value p, and it is used in the same way, its behavior will also change with different object types.

Since the named type itself cannot have methods, but only its reference can have methods (refer to Section 6.2), in the above example, v1 cannot be assigned to p, but only its reference &v1 can. If you try to assign v1 to p, a compilation error will occur.