Generics are an indispensable mechanism for programming languages.
Templates are used to implement generics in C++ language, but there is no mechanism of generics in C language, which makes it difficult for C languageto build complex projects.
Generic mechanism is a mechanism used by programming languages to express type abstraction, which is generally used for classes with functions determined and data types to be determined, such as linked lists, mapping lists, and so on. This is a method of selecting and sorting integer numbers: Running result: This is a simple maximum program that can be used to process data of the i32digital type, but not for data of the f64 type. By using generics, we can make this function available to all types. But in fact, not all data types can be compared to size, so the next piece of code is not used to run, but to describe the syntax format of function generics: The Option and Result enumeration classes we learned earlier are generic. Both structures and enumerated classes in Rust can implement generic mechanisms. This is a point coordinate structure, and T represents the numerical type that describes the point coordinates. We can use it like this: The type is not declared when it is used. The automatic type mechanism is used here, but type mismatches are not allowed as follows: When x is bound to 1, T is set to i32, so the type of f64 is no longer allowed. If we want x and y to be represented by different data types, we can use two generic identifiers: Methods that represent generics in enumerated classes such as Option and Result: Both structures and enumerated classes can define methods, so methods should also implement generic mechanisms, otherwise generic classes will not be effectively manipulated by methods. Running result: Be careful, The generics of the Method The concept of trait is close to the Interface in Java, but the two are not exactly the same. Features are similar to interfaces in that they are behavioral specifications that can be used to identify which classes have which methods. Features are used in Rust We use it to implement a structure: The format is: Rust can implement multiple features of the same class, each This is the difference between a property and an interface: an interface canonly standardize a method, not define a method, but a property can define amethod as the default method, and because it is “default”, the object canredefine the method or not redefine the method using the default method: Running result: If we put In many cases, we need to pass a function as an argument, such as a callbackfunction, setting button events, and so on. In Java, the function must be passed as an instance of the class implemented by the interface, and it can be achieved by passing property parameters in Rust: Anything that comes true Property parameters can also be implemented using this equivalent syntax: This is a syntactic sugar with a style similar to generics, which is useful when multiple parameter types are properties: If multiple properties are involved in the type representation of a property, it can be represented by a + symbol, for example: Note: when used only to represent types, it does not mean that it can be used in the Complex implementation relationships can be used Can be simplified to: After understanding this syntax, the “take the maximum” case in the generic chapter can be really implemented: Running result: Tip: due to the need to declare The format of the return value of the property is as follows: However, the return value of the property only accepts the object that implements the property as the return value, and all possible return values in the same function must be of exactly the same type. For example, both structure An and structure B implement features. This code declares 7.17.1. Define generics in a function #
Example #
fn max(array: &[i32]) -> i32 {
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i] > array[max_index] {
max_index = i;
}
i += 1;
}
array[max_index]
}
fn main() {
let a = [2, 4, 6, 3, 1];
println!("max = {}", max(&a));
}
max = 6
Example #
fn max<T>(array: &[T]) -> T {
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i] > array[max_index] {
max_index = i;
}
i += 1;
}
array[max_index]
}
7.17.2. Structures and generics in enumerated classes #
struct Point<T> {
x: T,
y: T
}
let p1 = Point {x: 1, y: 2};
let p2 = Point {x: 1.0, y: 2.0};
let p = Point {x: 1, y: 2.0};
struct Point<T1, T2> {
x: T1,
y: T2
}
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
Example #
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("p.x = {}", p.x());
}
p.x = 1
impl
keyword must be followed by
<T>
because the T behind it follows its example. But we can also add methods to one of these generics:impl Point<f64> {
fn x(&self) -> f64 {
self.x
}
}
impl
block itself do not hinder the ability of its internal methods to be generic:impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
mixup
put a
Point<T,
U>
the x and the x of the point
Point<V,
W>
. The y of the point is merged into a type of
Point<T,
W>
,it’s new. 7.17.3. Characteristics #
trait
indicates:trait Descriptive {
fn describe(&self) -> String;
}
Descriptive
is stipulated that the implementer must have
describe(&self)
->
String
method.Example #
struct Person {
name: String,
age: u8
}
impl Descriptive for Person {
fn describe(&self) -> String {
format!("{} {}", self.name, self.age)
}
}
Impl<attribute name>for<implemented type name>
impl
only one block can be implemented. 7.17.4. Default Properties #
Example #
trait Descriptive {
fn describe(&self) -> String {
String::from("[Object]")
}
}
struct Person {
name: String,
age: u8
}
impl Descriptive for Person {
fn describe(&self) -> String {
format!("{} {}", self.name, self.age)
}
}
fn main() {
let cali = Person {
name: String::from("Cali"),
age: 24
};
println!("{}", cali.describe());
}
Cali 24
impl
Descriptive
for
Person
, if the content in the block is removed, the running result is:[Object]
7.17.5. Characteristics as parameters #
fn output(object: impl Descriptive) {
println!("{}", object.describe());
}
Descriptive
, the object of the property can be used as an argument to this function. It is not necessary for this function to know whether there are any other properties or methods in the incoming object, just know that it must have
Descriptive
. The method of feature specification is fine. Of course, other properties and methods cannot be used within this function.fn output<T: Descriptive>(object: T) {
println!("{}", object.describe());
}
fn output_two<T: Descriptive>(arg1: T, arg2: T) {
println!("{}", arg1.describe());
println!("{}", arg2.describe());
}
fn notify(item: impl Summary + Display)
fn notify<T: Summary + Display>(item: T)
impl
block.
where
keyword simplification, for example:fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
Example #
trait Comparable {
fn compare(&self, object: &Self) -> i8;
}
fn max<T: Comparable>(array: &[T]) -> &T {
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i].compare(&array[max_index]) > 0 {
max_index = i;
}
i += 1;
}
&array[max_index]
}
impl Comparable for f64 {
fn compare(&self, object: &f64) -> i8 {
if &self > &object { 1 }
else if &self == &object { 0 }
else { -1 }
}
}
fn main() {
let arr = [1.0, 3.0, 5.0, 4.0, 2.0];
println!("maximum of arr is {}", max(&arr));
}
maximum of arr is 5
compare
the second argument to the function must be the same as the type that implements the property, so
Self
(note case) the keyword represents the current type (not the instance) itself. 7.17.6. Property as the return value #
Example #
fn person() -> impl Descriptive {
Person {
name: String::from("Cali"),
age: 24
}
}
Trait
the following function is wrong:Example #
fn some_function(bool bl) -> impl Descriptive {
if bl {
return A {};
} else {
return B {};
}
}
7.17.7. Conditional realization method #
impl
is so powerful that we can use it to implement the methods of the class. But for a generic class, sometimes we need to distinguish betweenthe methods that the generic class to which it belongs has been implementedto determine which method it should implement next:struct A<T> {}
impl<T: B + C> A<T> {
fn d(&self) {}
}
A<T>
type can effectively implement this only ifT has already implemented the B and C features with
impl
block.