Following the RDB file parsing function mentioned last time, after the data parsing step is completed, the next problem is how to save the parsed data, Redis has a variety of data types, string, hash, list, zset, set, at the beginning of the scheme is to define a data structure for each type of data, according to different data types, Save the data to different data structures, but this introduces a lot of redundant code. For example, string and hash, the initial code looks like this:

Type Rdb struct {... StrObj map[string]string hashObj map[string]map[string] String... Func (r *Rdb) saveStrObj(redisKey string, // Save hash function func (r *Rdb) saveHashObj(redisKey string) {r.strobj [redisKey] = redisVal} hashField string, hashVal string) { item, ok := r.hashObj[redisKey] if ! ok { item = make(map[string]string) r.hashObj[redisKey] = item } item[hashField] = hashVal }Copy the code

This method has a lot of redundant code, such as save string and save hash structure need to write two sets of similar code, and in the initialization of Rdb structure, also need to initialize all structures, and then passed to the initialization function of Rdb, such as:

StrObj := make(map[string]string) hashObj := make(map[string]map[string]string) RDB := & RDB {... , strObj, hashObj}Copy the code

Such code can be cumbersome to write and difficult to maintain, and in projects with more data types, it can seem outrageous. For example, in this practice, redis data are key-value pairs, the key type is fixed – string, but the value type is map, String and so on, so I wonder if there is a generics technology to help achieve the desired function.

Generic programming

Generic programming is a style or paradigm of programming languages. Generics allow programmers to write code in strongly typed programming languages using types that are specified later and specified as parameters when instantiated. (From Wikipedia)

To put it simply, generic programming means not programming for a specific type, a method that works not for a few specific data types, but for most data types.

For example, the development of an addition function, not only to support integer addition, floating point, string, array, and other types of addition, can be implemented.

Before I get into the generic programming implementation of Go, I’d like to talk a little bit about the generic implementation of C, which is again my favorite language.

C language generic implementation

Void * : void * : void * : void * : void * : void * : void * : void * : void *

Void swap(void *p1, void *p2) {size_t size = (sizeof(p1) == sizeof(p2))? sizeof(p1) : -1; char temp[size]; memcpy(temp, p1, sizeof(p1)); memcpy(p1, p2, sizeof(p2)); memcpy(p2, temp, sizeof(temp)); }Copy the code

So, with the generic version of the exchange function, verify by performing an exchange of integers, floats, and strings:

int main()
{
    int a = 1;
    int b = 42767;
    swap(&a, &b);
    
    float f1 = 1.234;
    float f2 = 2.345;
    swap(&f1, &f2);

    char str1[6] = "hello";
    char str2[10] = "world ooo";
    swap(str1, str2);

    printf("a: %d, b: %d\n", a, b);
    printf("f1: %f, f2: %f\n", f1, f2);
    printf("str1: %s, str2: %s\n", str1, str2);
}
Copy the code

The result is as follows:

The key to the implementation of the generic version of the exchange function is void * and memcpy function, is a copy of the memory operation, because the data in the memory are saved binary, as long as the type of operation exchange is consistent, then through memcpy will copy the type of byte size data, so as to achieve the same type of data exchange. It is important to note that generic programming in C is not safe, such as in this exchange function, if the exchange of different types of data, such as short and int exchange:

short a = 1;
int b = 5;
swap(&a, &b);
Copy the code

This call is error-free and runnable, but the result of the swap is dependent on the system’s byte order, which is meaningless and requires more checking and special judgment by the programmer.

Go language generics

Interface {} is also a type. As long as the methods in interface{} are implemented, they can be classified as the same type. Empty interface{} has no methods. Then any type can be the same class (somewhat like Java’s Object, the superclass of all classes).

interface{}

Interface {} is a type of Go language, which can be understood as Java interface type by analogy. In Go language, interface{} defines a method set. As long as the method set in interface{} is implemented, it can be said that the interface is implemented. The interface{} type of the Go language is a static data type that is checked at compile time, but it is also a dynamic data type because it can be used to hold multiple types of data.

Interface {} of the Go language provides a use for duck typing, which works like dynamic data types in PHP, but the compiler still fails if you try to store ints with an interface{} declared by other methods.

For example, what happens when you use interface{} instead of the code at the beginning?

Defines a RedisObject structure that holds the type, length, and value of the Redis object. The value uses an empty interface{} type:

type RedisObject struct {
    objType int
    objLen  int
    objVal  interface{}
}
Copy the code

When saving a value, simply assign the value directly to a RedisObject:

func (r *Rdb) saveStrObj(redisKey string, strVal string) { redisObj := NewRedisObject(RDB_TYPE_STRING, r.loadingLen, strVal) r.mapObj[redisKey] = redisObj } func (r *Rdb) saveHash(hashKey string, hashField string, hashValue string) { item, ok := r.mapObj[hashKey] if ! ok { tmpMap := make(map[string]string) item = NewRedisObject(RDB_TYPE_HASH, 0, tmpMap) r.mapObj[hashKey] = item } item.objVal.(map[string]string)[hashField] = hashValue }Copy the code

For a string type, its value is a simple string, which can be assigned by the statement R.mapobj [redisKey] = redisObj. For a hash object, it is more complicated. First, check whether the object that holds the hashKey is a valid object. (map[string]string); (map[string]string); (map[string]string); Parse objVal of type interface{} into a map[string][string] value.

Types of assertions

The technique above for casting objVal is called type assertion, which is a technique for converting from type to type. Unlike casting, type assertion is done between interfaces.

grammar

"The value of the target type >, < > Boolean parameters: = > < expression. (target type) / / safety type asserts that" the value of the target type > : = > < expressions. (the target type) & emsp; &emsp; // Insecure type assertionCopy the code

If the assertion fails, panic will occur. In order to prevent excessive panic, you need to make some judgment before assertion, which is the difference between secure and insecure assertions. Secure type assertions can obtain Boolean values to determine whether the assertion succeeds.

In addition, you can get the specific type of the variable by calling t.(type).

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
    default:
        fmt.Printf("unexpected type %T", t)       // %T prints whatever type t has
    case bool:
        fmt.Printf("boolean %t\n", t)             // t has type bool
    case int:
        fmt.Printf("integer %d\n", t)             // t has type int
    case *bool:
        fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
    case *int:
        fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}
Copy the code

conclusion

Through this practice, in addition to have more knowledge of generic programming, learned the language of the generic programming principle, realized the interface {} is a bright spot in the language, and at the same time also know something about the nature of the underlying operating computer data, the data in the process is at the bottom is a pile of binary, parse the data is not to identify the type of the data, Instead, the program reads the corresponding byte based on the type of the variable and parses it in a different way. Types are just different ways of reading memory.

Original article, writing is limited, talent and learning shallow, if the article is not straight, hope to inform.

If this article was helpful to you, please click “like”, thanks ^_^

More exciting content, please pay attention to the individual public number.