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.1. Null interface - universal wrapper

In Wa-lang, the simplest interface is the null interface, namely interface {}. The method of declaring interface type variables is the same as other types. For example, the following code declares an null interface variable named i:

    i: interface{}

Traditionally, we generally refer to interface type variables as interface values. The null interface has a very unique feature: Any type of value can be assigned to the null interface value. For example, the following operations are all legal:

    iface: interface{}

    iface = 777         // Assigning untyped integer to null interface
    iface = 13.14       // Assigning an untyped floating point number to an null interface
    iface = "你好,空接口" // Assign string to null interface

    i: i64 = 58372665865
    iface = i // Assign 64-bit integer to null interface

    // Assign anonymous structure value to null interface:
    iface = struct{name: string; age: i32}{name: "凹语言", age: 1}

This assignment behavior performs a value copy operation, which is equivalent to copying a copy of the original data in the interface value. This copy is called the concrete value of the interface value, and the type of the concrete value is called concrete type.

So how to determine the concrete type of an assigned interface value? How to read concrete value? This requires the use of type assertion syntax, whose general form is:

    v, ok = iface.(Type) // Assert whether the concrete type of iface is Type

Among them, v is a value of type Type, and ok is a value of type bool. After the statement is executed, if ok is true, it means that the concrete type of the interface value iface is indeed Type, and its concrete value will be assigned to v; otherwise it indicates that the concrete type of iface is not Type. A example is as follows:

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

type T1 struct {
    a: i32
}

func main {
    ival: i32 = 777
    printConcrete(ival)       // i32: 777
    printConcrete("你好凹语言") // string: 你好凹语言

    v1 := T1{a: 42}
    printConcrete(v1) // T1, T1.a: 42

    printConcrete(13.14) // unknown type
}

func printConcrete(iface: interface{}) {
    ok: bool
    i: i32
    s: string
    t: T1

    i, ok = iface.(i32)
    if ok {
        println("i32:", i)
        return
    }

    s, ok = iface.(string)
    if ok {
        println("string:", s)
        return
    }

    t, ok = iface.(T1)
    if ok {
        println("T1, T1.a:", t.a)
        return
    }

    println("unknown type")
}

In the function printConcrete, through interface type assertion, the concrete type of the incoming null interface value can be dynamically determined and its concrete value can be obtained. Since there is no floating point assertion within the function, "unknown type" is output when a floating point number is entered.

Note that the parameter type of the function printConcrete is an null interface (interface{}). When calling it in the main function, an implicit conversion (copy) is actually performed. For example, the statement printConcrete(ival) actually Equivalent to:

    iface: interface{} = ival
    printConcrete(iface)

Wa-lang do not allow implicit type conversion in most cases, but interfaces are an exception. When the function parameter type is an interface, if the actual parameter filled in by the caller is a concrete type, the compiler will automatically perform assignment conversion.

If there are multiple possibilities for the concrete type of the interface value, then using multiple type assertions plus conditional judgment will undoubtedly be cumbersome. In this scenario, you can use branch type assertions in the format of switch...case.... For example, the above printConcrete function can be rewritten as:

func printConcrete(iface: interface{}) {
    //branch type assertion
    switch v := iface.(type) {
    case i32:
        println("i32:", v)  // v is the concrete value of iface. Under this branch, its type is i32, the same below.

    case string:
        println("string:", v)

    case T1:
        println("T1, T1.a:", v.a)

    default:
        println("unknown type")
    }
}

Among them, iface.(type) is a fixed writing method, and each subsequent case branch indicates that the concrete type satisfies the branch condition.

Any type of value can be assigned to the null interface. It actually plays the role of a universal wrapper in Wa-lang and is often used to transfer values whose types change dynamically between functions.

In this article, "null interface" refers to interface{}, which is an interface type whose method set is empty. "non-null interface" in the next section refers to an interface type whose method set is not empty; When we want to describe an interface value with a value of 0, we will use "0 value interface", or "nil interface", please pay attention to the distinction.