In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the relevant knowledge of "how to master Rust language". In the operation of actual cases, many people will encounter such a dilemma. Next, let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
A Guide to the Rust Development Environment
1.1 Rust code execution
According to the knowledge of compilation principle, the compiler does not directly translate the source language into the target language, but into an "intermediate language", which the compiler practitioners call "IR"-instruction set, and then translates from the intermediate language to the assembly language of the target platform using back-end programs and devices.
Rust code execution:
1) after word segmentation and parsing, the Rust code generates AST (abstract syntax tree).
2) then the AST is further simplified to HIR (High-level IR) in order to make it easier for the compiler to do type checking.
3) HIR will be further compiled to MIR (Middle IR), which is an intermediate representation, the main purpose of which is:
A) reduce compilation time
B) reduce implementation time
C) more accurate type checking.
4) eventually MIR will be translated into LLVM IR and then compiled by LLVM processing into target machine code that can run on various platforms.
Angular IR: intermediate language
Angular HIR: high-level intermediate language
Angular MIR: intermediate intermediate language
·LLVM: Low Level Virtual Machine, the underlying virtual machine.
LLVM is the framework system of Architectural Compiler (compiler), written in C++, and used to optimize compile time (compile-time), link time (link-time), run time (run-time) and idle time (idle-time) of programs written in any programming language.
There is no doubt that the intermediate language IR of different compilers is different, and IR can be said to embody the characteristics of this compiler: his algorithm, optimization method, assembly flow and so on. If you want to fully grasp the work and running principle of a certain compiler, it is undoubtedly an important means to analyze and learn the intermediate language of this compiler.
Because the intermediate language acts as a bridge between the front end and the back end of the compiler, if we want to carry out the back-end migration based on LLVM, we undoubtedly need to develop the compiler back-end corresponding to the target platform. In order to complete this work smoothly, a thorough understanding of the intermediate language of LLVM is undoubtedly a very necessary work.
One of the great improvements of LLVM compared with gcc is that it greatly improves the generation efficiency and readability of the intermediate language. The intermediate language of LLVM is a format between c language and assembly language. It not only has the readability of the high-level language, but also can fully reflect the operation and transmission of the underlying data of the computer.
1.1.1 MIR
MIR is an abstract data structure based on control flow graph (Control Flow Graph,CFG), which contains all possible processes in the process of program execution in the form of directed graph (DAG). Therefore, MIR-based borrowing checking is called the life cycle of non-lexical scope.
MIR consists of the following key components:
L basic block (Basic block,bb), which is the basic unit of control flow graph
Get statement (statement)
ZJ termination sentence (Terminator)
L local variables, occupy the location of memory, such as function parameters, local variables, etc.
L location (Place) to identify an unknown amount expression in memory.
L right value (RValue), the expression that produces the value.
How it works can be found on pages 158 and 159 of Rust programming.
You can generate MIR code in http://play.runst-lang.org.
1.1 Rust installation
Method 1: see the introduction to the official installation section of Rust.
You can actually call this command to install: curl https://sh.rustup.rs-sSf | sh
Method 2: download the offline installation package to install it. For more information, please see the official Other Rust Installation Methods chapter of Rust.
1.2 Rust compilation & running 1.2.1 Cargo package management
Cargo is the package management tool in Rust, and the third-party package is called crate.
Cargo did four things:
L use two metadata (metadata) files to record various project information
Get and build the project's dependencies
Call rustc or other build tools with the correct parameters to build the project
L proposes a unified standard workflow for Rust ecosystem development
Cargo file:
L Cargo.lock: only records the details of the dependent package, which does not need to be maintained by the developer, but is automatically maintained by Cargo
L Cargo.toml: describes all the information needed for the project, including the dependencies of third-party packages
Cargo compilation defaults to Debug mode, where the compiler does not make any optimizations to the code. You can also use the-- release parameter to use the publish mode. In release mode, the compiler optimizes the code so that the compilation time is slower, but the code runs faster.
The official compiler, rustc, is responsible for compiling rust source code into executable files or other files (.a, .so, .lib, etc.). For example: rustc box.rs
Rust also provides a package manager Cargo to manage the entire workflow. For example:
Lcargo newfirst_pro_create: create a project named first_pro_create
Lcargo new-- libfirst_lib_create: create a library project for the command first_lib_create
Lcargo doc
Lcargo doc-open
Lcargo test
Lcargo test- test-threads=1
Lcargo build
Lcargo build-release
Lcargo run
Lcargo install-path
Lcargo uninstallfirst_pro_create
Lcargo new-bin use_regex
1.2.2 using third-party packages
Rust can add packages that you want to rely on under [dependencies] in Cargo.toml to use third-party packages.
Then in the src/main.rs or src/lib.rs file, use the extern crate command to declare that the package is introduced.
For example:
It is worth noting that you use extern crate to declare that the name of the package is linked_list, with an underscore "_", while in Cargo.toml you use the hyphen "-". In fact, Cargo converts hyphens to underscores by default.
Rust also does not recommend naming the package name with the suffix "- rs" or "_ rs" and will forcibly remove the suffix.
For details, see "Rust programming", page 323.
1.4 Rust Common commands 1.5 Rust Command Specification
Function: serpentine nomenclature (snake_case), for example: func_name ()
File name: serpentine nomenclature (snake_case), such as file_name.rs, main.rs
Temporary variable name: serpentine nomenclature (snake_case)
The name of a global variable:
Structure: big hump nomenclature, for example: struct FirstName {name: String}
Z enum type: big hump nomenclature.
Associated constants: constant names must be all capitalized. What is an association constant can be found on page 221 of Rust programming.
The _ Cargo converts the hyphen "-" to an underscore "_" by default.
It is also not recommended to name the package name with the suffix "- rs" or "_ rs", and will forcibly remove the suffix.
II. Rust Grammar 2.1Q & Summary 2.1.1 Copy semantics & & Move semantics (Move semantics must transfer ownership)
Types are becoming more and more abundant, and it is difficult for value types and reference types to describe the whole situation, so it is introduced:
Zero value semantics (Value Semantic)
After replication, the storage space owned by the two data objects is independent and does not affect each other.
The basic native types are value semantics, and these types are also called POD (Plain old data). POD types are all value semantics, but value semantic types are not necessarily POD types.
A native type with value semantics is copied bitwise by the compiler when it is assigned as a right value.
ZR reference semantics (Reference Semantic)
After replication, the two data objects are aliases to each other. Manipulating any one of these data objects affects the other.
Smart pointer Box encapsulates native pointers and is a typical reference type. Box cannot implement Copy, which means that it is marked by rust for referential semantics and prohibits bit-by-bit replication.
Reference semantic types cannot implement Copy, but you can implement the clone method of Clone for deep replication.
In Rust, you can distinguish between value semantics and reference semantics of data types by whether or not to implement Copy trait. But to be more precise, Rust also refers to new semantics: Copy semantics and Move semantics.
Z. Copy semantics: corresponding value semantics, which implements that the type of Copy is safe for bitwise replication.
Z. Move semantics: corresponds to reference semantics. Bitwise replication is not allowed in Rust, only mobile ownership is allowed.
2.1.2 which have implemented Copy
Z. structure: Copy is not automatically implemented when members are of replicated semantic types.
Z. enumerated body: Copy is not automatically implemented when the members are of replicated semantic types.
Structure & & enumeration body:
1) when all members are duplicated semantic types, you need to add the attribute # [derive (Debug,Copy,Clone)] to implement Copy.
2) if you have a member of the mobile semantic type, you cannot implement Copy.
Zugtuple type: Copy is implemented itself. If all elements are of replication semantic type, the default is bitwise replication, otherwise mobile semantics is performed.
* string literals & str: supports bit-by-bit replication. For example: C = "hello"; then c is the literal amount of the string.
2.1.3 which Copy is not implemented
The String string object: to_string () converts the literal amount of a string to a string object.
2.1.4 which have implemented Copy trait
Z. primitive integer type
For types that implement Copy, the clone method only needs to simply implement bit-by-bit replication.
2.1.5 which Copy trait is not implemented
Dead Box
What is the use of realizing Copy trait?
The type that implements Copy trait also has replication semantics, which will be copied bit by bit by default when assigning values or passing in functions.
For types that can be safely copied on the stack by default, you only need to copy by bit, and it is convenient to manage memory.
For data that can only be stored on the heap by default, deep replication must be performed. Deep replication requires re-opening space in heap memory, which leads to more performance overhead.
2.1.6 which ones are on the stack? Which are on the heap? 2.1.7 let binding
The bindings declared by @ Rust are immutable by default.
If you need to modify it, you can use mut to declare that the binding is mutable.
2.2 data types
The data types in many programming languages are divided into two categories:
Zero value type
It generally refers to the type in which all the data can be saved in the same location. For example, numeric values, Boolean values, structures, and so on are all value types.
The value types are:
L primitive type
L structure
L enumerated body
ZR reference type
There will be a pointer to the actual store. For example, some reference types usually store data in the heap, while only addresses (pointers) to the data in the heap are stored in the stack.
Reference types are:
L normal reference type
L native pointer type
2.2.1 basic data type Boolean type
The bool type has only two values: true and false.
Basic numeric type
The main focus is on the range of values, as detailed on page 26 of Rust programming.
Character type
Use single quotation marks to define the char type. The character type represents a Unicode scalar value, with 4 bytes per byte.
Array type
The type signature of the array is [T; N]. T is a generic tag that represents a specific type of element in an array. N represents the length of the array, and its value must be determined at compile time.
Array characteristics:
L fixed size
L elements are all of the same type
L immutable by default
Slice Typ
The Slice type is a reference fragment to an array. At the bottom, the slice represents a pointer to the start of the array and the length of the array. Use the [T] type to represent a continuous sequence, then the slice types are & [T] and & mutate [T].
For details, see "Rust programming", page 30.
Str string type
String type str, which usually exists in the form of immutable borrowing, namely & str (string slicing).
Rust divides strings into two types:
1) & str: fixed-length string
2) String: its length can be changed at will.
The & str string type consists of two parts:
1) A pointer to a sequence of strings
2) the value of the record length.
& str is stored on the stack and the sequence of str strings is stored in the program's static read-only data segment or heap memory.
& str is a fat pointer.
Never Typ
Never type, that is!. This type is used to represent a calculation type that can never have a return value.
Other (this part does not belong to the basic data type)
This section does not belong to the basic data type. Due to the orchestration problem, it is put here for the time being.
Fat pointer
Fat pointer: contains dynamic size type address information and a pointer that carries length information.
For details, see "Rust programming", page 54.
Zero size type
The characteristic of zero size types (Zero sized Type,ZST) is that their values are themselves, and the runtime does not take up memory space.
The size of the cell type and cell structure is zero, and so is the size of the array made up of the cell type.
The meaning of the ZST type is "empty".
Bottom type
The bottom type is actually the introduced never type, indicated by an exclamation point (!). It is characterized by:
L has no value
L is a subtype of any other type
If the ZST type means "empty", then the bottom type means "none".
The base type has no value, and it can be equivalent to any type.
For details, see "Rust programming", page 57.
2.2.2 compound data type tuple
Rust provides 4 composite data types:
L tuple (Tuple)
L structure (Struct)
L enumerated body (Enum)
L complex (Union)
Let's introduce tuples first. A tuple is a kind of heterogeneous finite sequence, which is shaped like (T ~ ~ U ~ M ~ N). Heterogeneity means that elements within a tuple can be of different types. By finite, it means that the tuple has a fixed length.
L empty tuple: ()
When l has only one value, you need to add a comma: (0,)
Structural body
Rust provides three structures:
L named structure
L-tuple structure
L unit structure
For example:
A named structure:
Struct People {name: & 'static str,}
Zugtuple structure: the field has no name, only type:
Struct Color (i32, i32, i32)
When a tuple structure has only one field, it is called the New Type pattern. For example:
Struct Integer (U32)
A cell structure: a structure without any fields. An instance of a unit structure is itself.
Struct Empty; structure update syntax
Create a new instance from another instance using the Struct update syntax (..). You can use struct update syntax when the new instance uses most of the values of the old instance. For example:
# [derive (Debug,Copy,Clone)] struct Book out of order
BTreeMap = > ordered
Where HashMap requires that key be a hashable type, and the key of BTreeMap must be sortable.
Value must be a type of known size at compile time.
Example:
Use std::collections::BTreeMap;use std::collections::HashMap; let mut hmap = HashMap::new (); let mut bmap = BTreeMap::new (); hmap.insert (1, "a"); bmap.insert (1, "a"); sets: HashSet and BTreeSet
HashSet and BTreeSet are actually specific types that HashMap and BTreeMap set Value to empty tuples.
The elements in the collection should be unique.
The elements in HashSet are all hashable types, and the elements in BTreeSet must be sortable.
L HashSet should be unordered and BTreeSet should be ordered.
Example:
Use std::collections::BTreeSet;use std::collections::HashSet; let mut hset = HashSet::new (); let mut bset = BTreeSet::new (); hset.insert ("This is a hset."); bset.insert ("This is a bset"); priority queue: BinaryHeap
The priority queue provided by Rust is based on binary maximum heap (Binary Heap).
Example:
Use std::collections::BinaryHeap; let mut heap = BinaryHeap::new (); heap.peek (); = > peek is the largest element in the fetch heap heap.push (98); capacity (Capacity) and size (Size/Len)
Whether it's Vec or HashMap, using these collection container types, the most important thing is to understand Capacity and Size/Len.
Capacity refers to the amount of memory allocated to the collection container.
Size refers to the number of elements contained in the collection.
2.2.4 Rust string
Rust strings are divided into the following types:
L str: represents a fixed-length string
L String: represents a string that can be grown
L CStr: represents a string allocated by C and borrowed by Rust. This is for compatibility with windows systems.
L CString: represents a C string assigned by Rust and passed to the C function, also used to interact with the C language.
L OsStr: represents a string related to the operating system. This is for compatibility with windows systems.
L OsString: represents a variable version of OsStr. Rust strings can be exchanged with each other.
L Path: represents the path, defined in the std::path module. Path wraps OsStr.
L PathBuf: paired with Path, which is a variable version of path. PathBuf wraps OsString.
Str belongs to the dynamic size type (DST), and its size cannot be determined at compile time. So the most common type in the program is the Slice type of str & str.
& str represents an immutable sequence of UTF-8 bytes that cannot be appended or changed after creation. Strings of type & str can be stored anywhere:
Dead static storage area
ZH heap allocation
Stack allocation
For details, see "Rust programming", page 249.
The String type is essentially a structure whose member variables are of type Vec, so it stores the character content directly in the heap.
The String type consists of three parts:
A pointer that executes a sequence of bytes in the heap (as_ptr method)
The byte length of the byte sequence in the record heap (len method)
The capacity allocated by the Z heap (capacity method)
2.2.4.1 string handling
Strings in Rust cannot use indexes to access their characters, and you can use bytes and chars methods to return iterators iterated by byte and character, respectively.
Rust provides two other methods: get and get_mut to get string slices by specifying an index range.
For details, see "Rust programming", page 251.
2.2.4.2 string modification
Append strings: push and push_str, and extend iterator
Insert string: insert and insert_str
Connection string: String implements two trait, Add and AddAssign, so you can use "+" and "+ =" to connect the string
Update string: through an iterator or some unsafe method
Delete strings: remove, pop, truncate, clear and drain
For details, see "Rust programming", page 255.
2.2.4.3 string lookup
Rust provides a total of 20 methods covering the following string matching operations:
Judgement of the existence of Zero
Zero position matching
ZGD split string
ZR capture matching
ZD delete match
Zero substitution matching
For details, see "Rust programming", page 256.
2.2.4.4 Type conversion
Z. parse: converts a string to the specified type
Zugt format! Macros: converting other types to strings
2.2.5 formatting rules
L fill string width: {: 5}, 5 means the width is 5
L intercept string: {: .5}
L align strings: {: >}, {: ^}, {: bool;} impl Fly for Duck {fn fly (& self)-> bool {return true;}} impl Fly for Pig {fn fly (& self)-> bool {return false }}
A detailed description of static and dynamic distribution can be found on page 46 of Rust programming.
Trait qualification
The following need to continue to in-depth understanding of the third chapter and summary. It will be added later.
Trait object label traitCopy traitDeref dereferencing as operator From and Into2.4 pointer 2.3.1 referencing Reference
Create with the & and & mut operators. Subject to the security check rules of Rust.
Reference is a pointer semantics provided by Rust. A reference is an implementation based on a pointer. The difference between a pointer and a pointer is that a pointer holds its address to memory, while a reference can be seen as an Alias of a block of memory.
In the ownership system, the reference & x can also be called a Borrowing of x. The ownership lease is completed through the & operator.
2.3.2 Native pointer (bare pointer)
* const T and * mut T. It can be used freely under the unsafe block and is not subject to the security check rules of Rust.
2.3.3 Smart pointer
It's actually a structure, but it behaves like a pointer. Smart pointers are a layer of encapsulation of pointers, providing some additional functions, such as automatically freeing heap memory.
Smart pointer differs from conventional structures in that it implements the trait of Deref and Drop.
Angular Deref: provides the ability to understand references
^ Drop: provides the ability to automatically destruct
2.3.3.1 what are the smart pointers
Smart pointers have ownership of resources, while ordinary references are just borrowings of ownership.
The values in Rust are assigned to stack memory by default. Values can be boxed (allocated in heap memory) through Box.
Dead String
Dead Vec
Values of type String and type Vec are allocated to heap memory and return pointers, and Deref and Drop are implemented by wrapping the returned pointers.
Dead Box
Box is a smart pointer to a heap memory allocation value of type T. When Box is out of scope, its destructor is called, internal objects are destroyed, and memory in the heap is automatically freed.
Dead Arc
Dead RC
Single-thread reference count pointer, which is not a thread-safe type.
Multiple ownership can be shared with multiple variables, and each time an ownership is shared, the count is incremented. For details, see "Rust programming", page 149.
Dead Weak
Is another version of RC.
Reference ownership shared through the clone method is called a strong reference, and RC is a strong reference.
The pointer to the Weak share has no ownership and is a weak reference. For details, see "Rust programming", page 150.
Dead Cell
Implement the situation of internal variability at the field level.
Suitable for copying semantic types.
Dead RefCell
Suitable for mobile semantic types.
Cell and RefCell are not smart pointers in nature, but simply provide containers that are internally immutable.
The scenarios most frequently used by Cell and RefCell are used with read-only references.
For details, see "Rust programming", page 151.
Dead Cow
Copy on write: a smart pointer to an enumerated body. Cow means "borrowing" and "owning" of ownership. The function of Cow is to access borrowed content in an immutable way and to clone a piece of data when variable borrowing or ownership is needed.
Cow is designed to reduce replication operations and improve performance. It is generally used in scenarios with more reads and less writes.
Another use of Cow is to unify the implementation specification.
2.3.4 dereferencing deref
Dereferencing will gain ownership.
Dereference operator: *
Which implement the deref method
Angular Box: the source code can be found on page 147 of Rust programming.
^ Cow: means that the immutable method that contains the data can be called directly. The main points can be found on page 155 of Rust programming.
ZR
Box supports dereference movement, while Rc and Arc smart pointers do not support dereferencing movement.
2.4 ownership mechanism (ownership):
Each block of memory allocated in Rust has its owner, the owner is responsible for freeing and reading and writing to that memory, and each value can only have a unique owner at a time.
During the assignment operation, the ownership does not change for the replication semantic types that can implement Copy. For a compound type, whether it is copied or moved depends on the type of its member.
For example, if the elements of an array are basic numeric types, the array is copied semantically and is copied bit by bit.
2.4.1 Lexical scope (life cycle)
Match, for, loop, while, if let, while let, curly braces, functions, and closures will all create new scopes, and ownership of the corresponding bindings will be transferred, as detailed on page 129 of Rust programming.
The function itself is an independent lexical scope:
When copying a semantic type as a function parameter, it is copied bit by bit.
If mobile semantics is used as a function argument, ownership is transferred.
2.4.2 non-lexical scope declaration cycle
Borrowing rule: the borrower's life cycle cannot be longer than the lender's life cycle. Use cases can be found on page 157 of Rust programming.
Because the above rules often lead to inconvenience in actual development, non-lexical scope life cycle (Non-Lexical Lifetime,NLL) is introduced to improve it.
MIR is an abstract data structure based on control flow graph (Control Flow Graph,CFG), which contains all possible processes in the process of program execution in the form of directed graph (DAG). Therefore, MIR-based borrowing checking is called the life cycle of non-lexical scope.
2.4.2 ownership borrowing
The premise of using variable borrowing is that the binding variable of loan ownership must be a variable binding.
In the ownership system, the reference & x can also be called a Borrowing of x. The ownership lease is completed through the & operator. So the reference does not cause the transfer of ownership of the bound variable.
When the reference leaves the scope, it is the time when it returns the ownership.
An immutable loan (reference) cannot be lent as a variable loan again.
An immutable loan can be lent multiple times.
A variable loan can only be lent once.
Z. immutable borrowing and variable borrowing cannot exist at the same time, for the same binding.
The borrowed life cycle cannot be longer than the lender's life cycle. For a specific example, see page 136 of Rust programming.
Core principle: sharing is immutable, changeable and non-shared.
Because dereferencing takes ownership, you need to pay special attention when dereferencing mobile semantic types (such as & String).
2.4.3 Life cycle parameters
The compiler's borrowing checking mechanism cannot check cross-function borrowings because the validity of current borrowings depends on lexical scope. Therefore, developers are required to explicitly annotate borrowed lifecycle parameters.
2.4.3.1 explicit lifecycle parameters
The @ lifecycle parameter must begin with single quotation marks
The parameter name is usually lowercase, for example:'a
The @ lifecycle parameters are located after the reference symbol & and use spaces to separate the lifecycle parameters and types.
Labeling lifecycle parameters is caused by borrowed pointers. Because of borrowed pointers, when the function returns borrowed pointers, in order to ensure memory security, you need to pay attention to the lifetime of the borrowed memory.
Annotating the lifecycle parameter does not change the lifecycle of any reference, it is only used for the compiler's borrowing check to prevent the pointer from dangling. That is, the purpose of lifecycle parameters is to help borrow an inspector to validate legitimate references and eliminate dangling pointers.
For example:
& i32; = > reference &'a mut i32; = > callout lifecycle parameter references &'a lifecycle parameter's variable reference allows the use of &'a str;, it is also legal to use & 'static str;. For 'static: declare' static lifetime when borrowed pointers points to the static object. For example, static STRING: & 'static str = "bitstring"; 2.4.3.2 Life cycle parameters in function signatures
Fn foo {part: &'a str,}
Here the lifecycle parameter markers actually agree on a rule with the compiler:
The life cycle of a structure instance should be shorter than or equal to the life cycle of any member.
2.4.3.4 Lifecycle parameters in method definition
When the structure contains a reference type member, the life cycle parameters need to be annotated, and the life cycle parameters need to be declared after the impl keyword and used after the structure name.
For example:
Impl {fn split_first (s: &'a str)-> &'a str {…}}
After adding the lifecycle parameter'a, the lifecycle length of the input reference is longer than the lifecycle length of the structure Foo instance.
Note: enumerators and structures treat lifecycle parameters in the same way.
2.4.3.5 static Lifecycle parameters
Static lifecycle 'static: is a special lifecycle built into Rust.' The static life cycle survives throughout the running of the program. All literals of a string have a life cycle and are of type & 'static str.
The literal quantity of a string is a global static type, its data and program code are stored together in the data segment of the executable file, and its address is known at compile time and is read-only and cannot be changed.
2.4.3.6 omitting life cycle parameters
Lifecycle parameters can be omitted when the following three rules are met. In this scenario, it is hard-coded into the Rust compiler so that the lifecycle parameters in the function signature can be automatically completed at compile time.
Lifecycle omission rules:
Each lifecycle omitted at the input location will become a different lifecycle parameter. That is, it corresponds to a unique life cycle parameter.
If there is only one input lifecycle location (whether omitted or not), the lifecycle will be assigned to the output lifecycle.
If there are multiple input lifecycle locations that contain & self or & mut self, then the self lifecycle will be assigned to the output lifecycle.
The above part of the rules are not fully understood, and you need to continue to be familiar with page 143 of Rust programming.
2.4.3.7 Lifecycle limit
Lifecycle parameters can be qualified as generics as trait does, in the following two forms:
L T:'a, which means that any reference in the T type should be as long as'a.
L T: Trait + 'a means that the T type must implement the Trait trait, and any reference in the T type must be "alive" as long as'a.
For a specific example, see "Rust programming", page 145.
2.4.3.8 Lifecycle of trait object
For a specific example, see "Rust programming", page 146.
2.4.3.9 High-level life cycle
Rust also provides a high-order life cycle (Higher-Ranked Lifetime) scheme, also known as higher-order trait qualification (Higher-Ranked Trait Bound,HRTB). This scheme provides for syntax.
The for syntax as a whole indicates that this lifecycle parameter is only for the "object" that follows it.
For details, please see page 192 of "Rust programming".
2.5 concurrent Security and ownership 2.5.1 tags trait:Send and Sync
If type T implements Send: it tells the compiler that instances of that type can safely transfer ownership between threads.
If type T implements Sync: it shows the compiler that instances of this type cannot cause memory insecurity in multithreaded concurrency, so they can be safely shared across threads.
2.5.2 which types implement Send2.5.3 and which types implement Sync2.6 native types
The built-in native types (primitive types) of Rust have the following categories:
L Boolean type: there are two values true and false.
L character type: represents a single Unicode character, stored as 4 bytes.
L numerical type: divided into signed integers (i8, i16, i32, i64, isize), unsigned integers (U8, U16, U32, U64, usize) and floating point numbers (F32, F64).
L string type: the bottom one is the variable length type str, more commonly used are string slices & str and heap allocation string String, where string slices are statically allocated, have a fixed size, and are immutable, while heap allocation strings are mutable.
L array: has a fixed size, and the elements are of the same type, which can be expressed as [T; N].
L slice: refers to part of the data of an array and does not need to be copied, which can be expressed as & [T].
L tuple: an ordered list with a fixed size, each element has its own type, and the value of each element is obtained by deconstructing or indexing.
L pointer: at the bottom are the bare pointers const T and mut T, but dereferencing them is not safe and must be placed in the unsafe block.
L function: a variable with a function type is essentially a function pointer.
L meta type: that is, (), and its unique value is also ().
2.7 function 2.7.1 function parameters
When function arguments are passed by value, ownership is transferred or Copy semantics are performed.
When function parameters are passed by reference, ownership does not change, but lifecycle parameters are required (indications that do not need to be displayed if the rules are met).
2.7.2 function parameter pattern matching
L ref: use pattern matching to get immutable references to parameters.
L ref mut: use pattern matching to get variable references to parameters.
In addition to ref and ref mut, function parameters can also be ignored using wildcards.
For details, see "Rust programming", page 165.
2.7.3 generic function
The function parameter does not specify a specific type, but uses the generic T, which has only one Mult trait limit for T, that is, only the type that implements Mul can be used as a parameter, thus ensuring type safety.
Generic functions do not specify a specific type, but rely on the compiler to infer automatically. If you are using basic primitive types, it is easier for the compiler to infer. If the compiler cannot automatically infer, you need to explicitly specify the type of function call.
2.7.4 methods and functions
Method represents the behavior of an instance object, and the function is just a simple piece of code that can be called by name. The method is also called by name, but it must be associated with a method recipient.
2.7.5 higher order function
A high-order function is a function that takes a function as a parameter or a return value, which is the most basic feature of a functional programming language.
For details, see "Rust programming", page 168.
2.8closure Closure
Closure usually refers to lexical closure, which is a function that holds external environment variables.
The external environment refers to the lexical scope in which the closure is defined.
External environment variables, also known as free variables in the functional programming paradigm, are variables that are not defined in the closure.
The function that binds a free variable to itself is a closure.
The size of the closure is unknown at compile time.
2.8.1 basic syntax of closures
The closure consists of pipe characters (two symmetrical vertical bars) and curly braces (or parentheses).
The parameter of the closure function is in the pipe character, which can be marked by the type after the colon or omitted like the normal function parameter. For example: let add = | a, b |-> i32 {a + b}
The body of the closure function is contained in the angular curly braces, and the curly braces and return values can also be omitted.
For example: let add = | a, b | a + b
When the closure function has no parameters and only captured free variables, the parameters in the pipeline can also be omitted.
For example: let add = | | a + b
2.8.2 implementation of closure
Closure is a kind of grammatical sugar. Closures are not the basic syntax elements provided by the Ruth language, but a layer of syntax that is convenient for developers to program on top of the basic syntax functions.
The difference between closures and ordinary functions is that closures can capture free variables in the environment.
Closures can be used as function parameters, which directly improves the abstract expression ability of Rust language. When passed as a function argument, it can be used as a trait qualification for generics or directly as a trait object.
Closures cannot be used directly as the return value of a function, and if you want to use a closure as a return value, you must use the trait object.
2.8.3 closure and ownership
The closure expression is automatically translated by the compiler into a structure instance, and one of the three trait Fn, FnMut, and FnOnce is implemented for it.
LFnOnce: transfers ownership of the method recipient. There is no ability to change the environment, it can only be called once.
L FnMut: variable borrowing is made to the recipient of the method. Have the ability to change the environment, can be called many times.
L Fn: immutable borrowing is made to the method recipient. Does not have the ability to change the environment, can be called multiple times.
If you want to implement Fn, you must implement FnMut and FnOnce
If you want to implement FnMut, you must implement FnOnce
If you want to implement FnOnce, you don't need to implement FnMut and Fn.
2.8.3.1 how to capture environment variables
For replicated semantic types, & immutable references are used to capture.
L for mobile semantic types, execute mobile semantics and transfer ownership to capture.
For mutable bindings, and operations to modify them are included in the closure, they are captured as mutable references (& mut T).
For details, see "Rust programming", page 178.
Rust uses the move keyword to force free variables in the environment defined by the closure to be transferred to the closure.
2.8.3.2 Rule Summary
If no environment variables are captured in the closure, Fn is implemented automatically by default.
If the environment variable of the replication semantic type is captured in the closure, then:
If you do not need to modify the environment variable, Fn is automatically implemented regardless of whether you use the move keyword or not.
If you need to modify the environment variable, FnMut is implemented automatically.
If the environment variable of the mobile semantic type is captured in the closure, then:
If you do not need to modify the environment variable and do not use the move keyword, FnOnce will be implemented automatically.
If you do not need to modify the environment variable and use the move keyword, Fn will be implemented automatically.
If you need to modify the environment variable, FnMut is implemented automatically.
When the closure of l FnMut uses the move keyword, the closure automatically implements Copy/Clone if the capture variable is of copied semantic type. If the capture variable is of a mobile semantic type, the closure does not automatically implement Copy/Clone.
2.9 iterator
Rust uses an external iterator, the for loop. External iterator: the entire traversal process can be controlled by the outside.
Trait is used in Rust to abstract iterator patterns. Iterator trait is an abstract interface to iterator patterns in Rust.
Iterators mainly include:
L next method: iterate over its internal elements
L Association type Item
L size_hint method: the return type is a tuple that represents the boundary information for the remaining length of the iterator.
Example:
Let iterator = iter.into_iter (); let size_lin = iterator.size_hint (); let mut counter = Counter {count: 0}; counter.next ()
The Iter type iterator, and the next method returns a value of type Option or Option. The for loop automatically calls the iterator's next method. The loop variable in the for loop gets the value of type & [T] or & mut [T] from the Option or Option type returned by next through pattern matching.
The loop variable generated by the Iter type iterator in the for loop is a reference.
The next method of the iterator of type IntoIter returns the Option type, and the loop variable generated in the for loop is a value, not a reference.
Example:
Let v = vec! [1,2,3]; for i in v {... } let v = vec! [1,2,3]; for i in v {... } let v = vec! [1,2,3]; for i in v {... } let v = vec! [1,2,3]; for i in v {... }
To ensure that the size_hint method can get accurate information about the iterator length, Rust introduces two trait, which are child trait of Iterator, both defined in the std::iter module.
L ExactSizeIterator: provides two additional methods len and is_empty.
L TrustedLen: like a tag trait, the length information obtained by size_hint is reliable as long as the iterator of TrustLen is implemented. The capacity check of the container is completely avoided and the performance is improved.
2.9.1 IntoIterator trait
If you want to iterate over an element in a collection container, you must convert it to an iterator before you can use it.
Rust provides two trait, FromIterator and IntoIterator, which are counteroperations to each other.
L FromIterator: can be converted from an iterator to a specified type.
L IntoIterator: can be converted from a specified type to an iterator.
Intoiter can use methods such as into_iter to get an iterator. The parameter of into_iter is self, which means that the method transfers ownership of the method recipient. There are two other iterators that do not have to transfer ownership. The details are as follows:
L Intoiter: transfer ownership, corresponding to self
L Iter: get immutable borrowing, corresponding to & self
L IterMut: get a variable loan, corresponding to & mut slef
2.9.2 which types of Iterator are implemented?
Only types that implement Iterator can be used as iterators.
Collection containers that implement IntoIterator can be converted to iterators through the into_iter method.
The collection containers that implement IntoIterator are:
LVec
Lumbago [T]
IntoIterator is not implemented for the [T] type by using mut [T] >
L
2.9.3 Iterator Adapter
Through the adapter pattern, you can convert one interface into another one you need. The adapter pattern enables incompatible types of interfaces to work together.
The adapter is also called a Wrapper.
Iterator adapters, all defined in the std::iter module:
L Map: a new iterator is generated by calling the specified closure on each element in the original iterator.
L Chain: create a new iterator by connecting two iterators.
L Cloned: create a new iterator by copying all the elements in the original iterator.
L Cycle: create an iterator that iterates forever, and when the iteration is complete, return to the first element to start the iteration.
L Enumerate: create an iterator that contains a count, which returns a tuple (iDie Val), where I is of type usize, the current index of the iteration, and val is the value returned by the iterator.
L Filter: create an iterator that filters elements with opportunity predicate judgment.
L FlatMap: create an iterator with a Map-like structure, but it won't contain any nesting.
L FilterMap: equivalent to the effect of using Filter and Map iterators at one time.
L Fuse: create an iterator that can traverse quickly. When traversing the iterator, None is returned once, and then all iterations are None. The iterator adapter can be used for optimization.
L Rev: create an iterator that can traverse backwards.
For more information, please see page 202 of Rust programming.
Rust can customize iterator adapters, as detailed on page 211 of Rust programming.
2.10 Consumer device
The iterator does not automatically traverse the behavior and needs to call the next method to consume the data in it. The most direct way to consume iterator data is to use for loops.
Rust provides a way to consume data within an iterator outside of the for loop, called a Consumer.
Consumers commonly used in the Rust standard library std::iter::Iterator:
L any: you can find out if there are elements in the container that meet the criteria.
L fold: this method takes two parameters, the first is the initial value, and the second is a closure with two parameters. The first parameter of the closure is called an accumulator, which accumulates the results of each iteration of the closure as the return value of the fold method.
L collect: specifically used to convert iterators to the specified collection type.
Lall
Lfor_each
Lposition
2.11 lock
L RwLock read-write lock: a multi-read single-write lock, also known as a shared exclusive lock. It allows multiple threads to read and a single thread to write. However, when writing, only one thread can hold the write lock; while reading, any thread is allowed to acquire the read lock. Read and write locks cannot be acquired at the same time.
L Mutex mutex: only a single thread is allowed to read and write.
III. Rust attribute
^ # [lang = "drop"]: Mark drop as a language item
·# [derive (Debug)]:
·# [derive (Copy, Clone)]:
·# [derive (Debug,Copy,Clone)]:
* # [lang = "owned_box"]: unlike native types, Box does not have a type name. It represents the particularity of smart pointers with unique ownership and needs to be specifically identified by lang item.
/
L fn_once: transfers ownership of the method recipient
L fn_mut: variable borrowing will be made to the method receiver
L fn: immutable borrowing is made to the method receiver
[lang = "rust_pareen_sugar"]: indicates special handling of parenthesis invocation syntax.
* # [must_use= "iterator adaptors are lazy."] Used to warn developers that the iterator adapter is lazy
IV. Memory Management 4.1 memory recovery
Drop-flag: automatically inserts Boolean tags in the function call stack for variables that leave scope, indicating whether the destructor is called, so that the destructor can be called at run time based on the tag made at compile time.
The type of Copy is implemented and there is no destructor. Because the type that implements Copy is copied, its life cycle is not affected by the destructor.
Need to continue to in-depth understanding of Chapter 4 and summary, to be supplemented later.
5. Unicode
The Unicode character set is equivalent to a table with each character corresponding to a non-negative integer called a code point (Code Point).
These code points are also divided into different types:
L scalar value
L proxy alignment point
L non-character code point
L reserve code point
L private code point
Scalar value refers to the actual existence of the corresponding character code point, its range is 0x0000~0xD7FF and 0xE000~0x10FFFF two segments.
Each character of the Unicode character set occupies 4 bytes and is stored in a sequence of symbols (Code Unit).
A symbol is the minimum combination of bits used to process and exchange encoded text.
Unicode character coding table:
L UTF-8 = > 1 byte symbol
L UTF-16 = > 2 byte symbol
L UTF-32 = > 4 byte symbol
The default text encoding format for the source file .rs of Rust is UTF-8.
This is the end of "how to master Rust language". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.