RUST Life of the Day: Generics

        Generic refers to the abstraction of a type into a "parameter", and both data and algorithms are implemented for this abstract type parameter, not for a specific type. When we need to actually use it, we need to specify and instantiate the type parameter. Using generics can write more abstract code and reduce the workload.

        Generics in Rust belong to static polymorphism, which is a kind of compile-time polymorphism. At compile time, both generic enumerations, generic functions and generic structures will be monomorphized. Monomorphization is a compiler strategy for static dispatch. 

        When a generic type is singulated with different type parameters, a completely different concrete type is obtained. For example, Option<i32> and Option<i64> are completely different types, and they cannot be used universally or converted to each other. When the compiler generates code, it generates different code for each different generic parameter.

generic container

        The most common generic application is the container. Vec<T> and HashMap<K, V> are all generic applications. For example, Vec<T> can specify the type of T as i32 or String in use - Vec<i32> or Vec<String>.

fn main() {
  let mut vec_int: Vec<i32> = vec![10, 20];
  vec_int.push(30);
}

generic enum

        A generic enum means that the enum value type is a generic type. Option<T> provided by the standard library is a widely used generic enumeration. Option<T> represents the abstract concept that may or may not have a value, Some(T) represents a possible value that can be of any type T, and None represents non-existence.

enum Option<T> {
 Some(T),
 None,
}

fn output(age:i32) -> Option<i32>{
    if age > 18{
        return Some(age)
    }else{
        return None
    }
}

fn main() {
 let ret1 = output(19);
 println!("{:?}",ret1);
 let ret2 = output(13);
 println!("{:?}",ret2);

}

generic structure

        A structure of generic type means that the field type of the structure is a generic type, and it can have one or more generic types. When defining a generic structure, you can add <T> or <T, U> after the structure name, and use T or U when defining the type of the field. We only use two generic types when defining Num<T, U>, and T and U can be the same or different. If it is Nu<T>, this definition indicates that the Nu<T> structure is common to a certain type T. The fields data and num both belong to this type. If we use different value types to create Nu<T> instances like the commented out code below, the code cannot be compiled.

#[derive(Debug)]
struct Num<T,U> {
    data: Option<T>,
    num: U
}
#[derive(Debug)]
struct Nu<T> {
    data: Option<T>,
    num: T
}
fn main() {
 let ret = Num{data:Some(1), num:"hello"};
 println!("{:?}",ret);
 //let ret = Nu{data:Some(1), num:"hello"};
 //println!("{:?}",ret);
}

generic function

        Both parameters and return values ​​of functions can be of generic type, and functions with parameters or return values ​​of generic type are called generic functions. Generic functions do not require all parameters to be generic, but only certain parameters can be generic. When defining a generic function, you can put <T> after the function name, and use T when defining the type of parameters or return values.

        Compiled foo(5) and foo("hello".to_string()) respectively generate corresponding foo functions, fn foo(x:i32)->i32 and fn foo(x: String)->String.

fn foo<T>(x: T) -> T {
 return x;
}


fn main() {
 println!("{}", foo(5));
 println!("{}", foo("hello".to_string()));
}

//fn foo(x: i32) -> i32 {
// return x;
//}

//fn foo<T>(x: String) -> String {
// return x;
//}

generic impl

        You can also use generics when impl. In the grammatical structure of impl<Trait>for<Type>{}, the generic type can appear in either the <Trait> position or the <Type> position. Like generics elsewhere, generics in the impl block are declared first and then used. Generic parameters that appear in the impl block need to be declared with angle brackets after the impl keyword.

#[derive(Debug)]
struct Container<T> {
    item: T
}

impl<T> Container<T> {
   fn new(item: T) -> Self {
        Container { item }
   }
}
fn main() {
    let c = Container::new(9);
    println!("{:?}",c);
}

Guess you like

Origin blog.csdn.net/xq723310/article/details/130439182