MindMap Gallery Mastering RUST 2nd Edition
This is a mind map about mastering RUST. Through the above steps and suggestions, you can gradually improve your Rust programming skills and enjoy the memory safety and efficient performance brought by Rust.
Edited at 2024-04-04 15:14:48One Hundred Years of Solitude is the masterpiece of Gabriel Garcia Marquez. Reading this book begins with making sense of the characters' relationships, which are centered on the Buendía family and tells the story of the family's prosperity and decline, internal relationships and political struggles, self-mixing and rebirth over the course of a hundred years.
One Hundred Years of Solitude is the masterpiece of Gabriel Garcia Marquez. Reading this book begins with making sense of the characters' relationships, which are centered on the Buendía family and tells the story of the family's prosperity and decline, internal relationships and political struggles, self-mixing and rebirth over the course of a hundred years.
Project management is the process of applying specialized knowledge, skills, tools, and methods to project activities so that the project can achieve or exceed the set needs and expectations within the constraints of limited resources. This diagram provides a comprehensive overview of the 8 components of the project management process and can be used as a generic template for direct application.
One Hundred Years of Solitude is the masterpiece of Gabriel Garcia Marquez. Reading this book begins with making sense of the characters' relationships, which are centered on the Buendía family and tells the story of the family's prosperity and decline, internal relationships and political struggles, self-mixing and rebirth over the course of a hundred years.
One Hundred Years of Solitude is the masterpiece of Gabriel Garcia Marquez. Reading this book begins with making sense of the characters' relationships, which are centered on the Buendía family and tells the story of the family's prosperity and decline, internal relationships and political struggles, self-mixing and rebirth over the course of a hundred years.
Project management is the process of applying specialized knowledge, skills, tools, and methods to project activities so that the project can achieve or exceed the set needs and expectations within the constraints of limited resources. This diagram provides a comprehensive overview of the 8 components of the project management process and can be used as a generic template for direct application.
Proficient in RUST
Chapter 1 Getting Started with Rust
1.1 What is Rust and why you need it
Rust is a fast, highly concurrent, safe, and empowering programming language originally created and released in 2006 by Graydon Hoare. Now it is an open source language, mainly maintained and developed by the Mozilla team and many open source community members.
Rust has an open source development website on GitHub, and its development momentum is very rapid. New features are added to the language through a community-driven Request For Comment (RFC) process, where anyone can submit new features and then describe them in detail in an RFC document. Consensus is then sought on the RFC, and if consensus is reached, the feature enters the implementation phase.
Rust exists as a statically and strongly typed language. Static attributes mean that the compiler has information about all relevant variables and types at compile time, and does a lot of checking at compile time, leaving only a small amount of type checking at runtime.
1.2 Install the Rust tool chain
rustup.rs
1.3 Introduction to Rust
1.3.1 Primitive types
bool
These are common Boolean values that can be either true or false.
char
character
integer
This type is characterized by bit width. The maximum length supported by Rust is 128 bits.
isize
A signed integer of variable size (size depends on underlying pointer size).
usize
Unsigned integer of variable size (size depends on underlying pointer size).
f32
f32-bit floating point type
f64
f64-bit floating point type
[T; N]
A fixed-size array, T represents the element type, N represents the number of elements, and is a non-negative constant at compile time.
[T]
A dynamically sized view of a contiguous sequence, T representing any type.
str
String slicing, mainly used as a reference, i.e. &str
(T, U, ..)
Finite sequences, T and U can be of different types.
fn(i32)->i32
A function that receives an i32 type parameter and returns an i32 type parameter. Functions also have a type.
1.3.2 Variable declaration and immutability
In Rust, we use the keyword let to declare variables. After a variable is initialized, it cannot be assigned another value. If you later need to point the variable to another variable (of the same type), you need to precede it with the keyword mut.
1.3.3 Function
Functions abstract a bunch of instructions into named entities that can later be called from other code and help users manage complexity.
fn add(a: u64, b: u64) -> u64 { a b } fn main() { let a: u64 = 17; let b = 3; let result = add(a, b); println!("Result {}", result); }
Functions are basically expressions that return a value, which by default is a value of type () (unit), which is similar to the void return type in C/C++.
1.3.4 Closure
Rust supports closures. Closures are similar to functions but have more information about the environment or scope in which they are declared. While functions have a name associated with them, closures' definitions do not, but they can be assigned to variables. Another advantage of Rust type inference is that in most cases you can specify parameters for closures without types. This is the simplest closure "let my_closure = ||();". We define a parameterless closure that does nothing. We can then call it via my_closure(), which is similar to a function. The two vertical bars "||" are used to store closure parameters, such as |a, b|. Sometimes it is necessary to specify the parameter type (|a:u32|) when Rust cannot figure out the correct type.
fn main() { let doubler = |x| x * 2; let value = 5; let twice = doubler(value); println!("{} doubled is {}", value, twice); let big_closure = |b, c| { let z = b c; z * twice }; let some_number = big_closure(1, 2); println!("Result from closure: {}", some_number); }
1.3.5 String
String is one of the most commonly used data types in any programming language. In Rust, they usually appear in two forms: &str type and String type. Rust strings are guaranteed to be valid UTF-8 encoded byte sequences. They are not NULL terminated like C strings, and can contain null bytes between strings.
fn main() { let question = "How are you?"; let person: String = "Bob".to_string(); let namaste = String::from("zd"); println!("{}! {} {}", namaste, question, person); }
In the above code, the type of person and namaste is String, and the type of question is &str. There are many ways to create String type data. String type data is allocated on the heap. &str type data is usually a pointer to an existing string. These strings are on the stack and heap, or they can be strings in the data segment of compiled object code.
1.3.6 Conditions and judgments
Conditional judgments in Rust are similar to those in other languages. They also follow a C-like if else structure.
fn main() { let rust_is_awesome = true; if rust_is_awesome { println!("Indeed"); } else { println!("Well, you should try Rust!"); } }
In Rust, the if construct is not a statement, but an expression. In general programming parlance, statements do not return any value, but expressions do. This distinction means that if else conditions in Rust always return a value. The value can be of type empty () or an actual value. Whatever the last line of curly braces is, it becomes the return value of the if else expression. It is important to note that if and else branches should have the same return type.
fn main() { let result = if 1 == 2 { "Wait, what?" } else { "Rust makes sense" }; println!("You know what? {}.", result); }
When the value to be assigned is returned from the if else expression, we need to use a semicolon as the end mark. For example, if is an expression, then let is a statement, which expects us to have a semicolon at the end.
1.3.7 match expression
Rust's match expressions are very simple and easy to use. It is basically similar to a simplified version of the switch statement in C language, allowing users to make judgments based on the value of the variable and whether there is advanced filtering functionality.
fn req_status() -> u32 { 200 } fn main() { let status = req_status(); match status { 200 => println!("Success"), 404 => println!("Not Found"), other => { println!("Request failed with code: {}", other); } } }
Each match must return the same type. Additionally, we must perform an exact match on all possible matching values. Rust allows us to ignore the remaining possibilities by using catch all variables (here other) or _ (underscore).
1.3.8 Loop
Doing something repeatedly in Rust can be accomplished using 3 constructs, namely loop, while, and for. In all these constructs, the keywords continue and break are usually included, allowing you to skip and break out of the loop respectively.
fn main() { let mut x = 1024; loop { if x < 0 { break; } println!("{} more runs to go", x); x -= 1; } }
An additional feature of executing loops in Rust is the ability to label blocks of loop code with names. This can be used in situations where you have two or more nested loops and want to break from any of them, not just for loops that directly contain break statements.
fn silly_sub(a: i32, b: i32) -> i32 { let mut result = 0; 'increment: loop { if result == a { let mut dec = b; 'decrement: loop { ifdec==0{ break 'increment; } else { result -= 1; dec -= 1; } } } else { result = 1; } } result } fn main() { let a = 10; let b = 4; let result = silly_sub(a, b); println!("{} minus {} is {}", a, b, result); }
Rust also has the keyword for, which is similar to the for loop used in other languages, but their implementation is completely different. Rust's for loop is basically syntactic sugar for a more powerful iteration construct (iterator). Simply put, for loops in Rust only work with types that can be converted to iterators. One such type is the Range type. The Range type can refer to a series of numbers.
fn main() { print!("Normal range: "); for i in 0..10 { print!("{},", i); } println!(); print!("Inclusive range: "); for i in 0..=10 { print!("(),", i); } }
1.3.9 Custom data types
Custom types are types defined by users. Custom types can be composed of several types. They can be wrappers around primitive types or a combination of multiple custom types. They come in three forms: structure, enumeration and union, also known as struct, enum and union. They allow you to represent your data more easily. The naming rules for custom types follow CamelCase.
Structure
In Rust, there are three forms of structure declaration. The simplest of these is the unit struct, which is declared using the keyword struct, followed by its name, and ending with a semicolon.
struct Dummy; fn main() { let value = Dummy; }
The second form of structure is the tuple struct, which has associated data. Each of these fields is not named, but is referenced based on their position in the definition.
struct Color(u8, u8, u8); fn main() { let white = Color(255, 255, 255); let red = white.0; let green = white.1; let blue = white.2; println!("Red value: {}", red); let orange = Color(255, 165, 0); let Color(r, g, b) = orange; println!("R: {}, G: {}, B: {} (orange)", r, g, b); let Color(r, _, b) = orange; }
Tuple structures are ideal when modeling data with less than 5 attributes. Any choice other than this will hinder code readability and reasoning. For data types with more than 3 fields, construct structures using C-like language. This is the third form and the most commonly used form.
struct Player { name: String, iq: u8, friends: u8, score: u16 } fn bump_player_score(mut player: Player, score: u16) { player.score = score; println!("Updated player stats:"); } fn main() { let name = "Alice".to_string(); let player = Player { name, iq: 171, friends: 134, score: 1129 }; bump_player_score(player, 120); }
enumerate
Enumerations are a good way to do this when you need to model different types of things. It is created using the keyword enum, followed by the enumeration name and a pair of curly braces. Inside the curly braces we can write all possible types, i.e. variants. These variants can be defined with or without data, and the data contained can be any far type, structure, tuple structure, or even an enumeration type.
enum Direction { N, E, S, W } enum PlayerAction { Move { direction: Direction, Speed: u8 }, Wait, Attack(Direction) } fn main() { let simulated_player_action = PlayerAction::Move { direction: Direction::N, speed: 2, }; match simulated_player_action { PlayerAction::Wait => println!("Player wants to wait"), PlayerAction::Move { direction, speed } => { println!("Player wants to move in direction {:?} with speed {}", direction, speed) } PlayerAction::Attack(direction) => { println!("Player wants to accack direction {:?}", direction) } }; }
1.3.10 Functions and methods on types
impl block on struct
We can add behavior to a defined structure using two mechanisms: constructor-like functions and getter and setter methods.
struct Player { name: String, iq: u8, friends: u8 } impl Player { fn with_name(name: &str) -> Player { Player { name: name.to_string(), iq: 100, friends: 100 } } fn get_friends(&self) -> u8 { self.friends } fn set friends(&mut self, count: u8) { self.friends = count; } } fn main() { let mut player = Player::with_name("Dave"); player.set_friends(23); println!("{}'s friends count: {}", player.name, player.get_friends()); let _ = Player::get_friends(&player); }
Associated method: This method does not have self type as the first parameter. It is similar to static methods in object-oriented programming languages. These methods are callable on the type itself and do not require an instance of the type to be called. An associated method is called by preceding the method name with the structure name and a double colon.
Instance method: a function that takes self as the first external parameter. The self here is similar to self in Python and points to the instance that implements the method.
impl blocks and enums
Enumerations are widely used in state machines, and when used with match expressions, they can make state transition code very concise. They can also be used for modeling of custom error types.
enum PaymentMode { Debit, Credit, Paypal } fn pay_by_credit(amt: u64) { println!("Processing credit payment of {}", amt); } fn pay_by_debit(amt: u64) { println!("Processing debit payment of {}", amt); } fn paypal_redirect(amt: u64) { println!("Redirecting to paypal for amount: {}", amt); } impl PaymentMode { fn pay(&self, amount: u64) { match self { PaymentMode::Debit => pay_by_debit(amount), PaymentMode::Credit => pay_by_credit(amount), PaymentMode::Paypal => paypal_redirect(amount) } } } fn get_saved_payment_mode() -> PaymentMode { PaymentMode::Debit } fn main() { let payment_mode = get_saved_payment_mode(); payment_mode.pay(512); }
1.3.11 module, import and use statements
Programming languages often provide a way to split large blocks of code into multiple files to manage complexity. Java follows the convention that each .java file is a public class, and C provides us with header files and include statements. Rust provides a module mechanism. Modules are a way of naming and organizing code in Rust programs.
Every Rust program requires a root module. For executable files, it is usually the main.rs file, and for libraries, it is usually the lib.rs file.
Modules can be declared inside other modules or organized into files and directories.
In order for the compiler to recognize our module, we need to use the keyword mod declaration. In our root module, we need to use the keyword use before the module name, which means bringing the element into the scope.
Elements defined in a module are private by default and need to be exposed to callers using the keyword pub.
1.3.12 Collection
array
Arrays have a fixed length and can store elements of the same type. They are represented by [T, N], where T represents any type and N represents the number of array elements. The size of the array cannot be represented by a variable and must be a literal value of usize.
tuple
Project list
key/value pair
slice
1.3.13 Iterator
subtopic
subtopic
subtopic
1.4 Improve character counter
1.5 Summary
Chapter 2 Using Cargo to manage projects
2.1 Package Manager
2.2 Modules
2.2.1 Nested modules
2.2.2 Using files as modules
2.2.3 Using directories as modules
2.3 Cargo and libraries
2.3.1 Create a new Cargo project
2.3.2 Cargo and dependencies
2.3.3 Using Cargo to execute tests
2.3.4 Using Cargo to run the example
2.3.5 Cargo workspace
2.4 Cargo tool extension
2.4.1 Subcommands and Cargo installation
2.4.2 Use clippy to format code
2.4.3 Introduction to Cargo.toml manifest file
2.5 Set up a Rust development environment
2.6 Use Cargo to build the imgtool program
2.7 Summary
Chapter 3 Testing, Documentation, and Benchmarking
3.1 Purpose of testing
3.2 Organizing tests
3.3 Unit testing
3.3.1 The first unit test
3.3.2 Running tests
3.3.3 Isolate test code
3.3.4 Failure testing
3..3.5 Ignore tests
3.4 Integration testing
3.4.1 First integration test
3.4.2 Sharing common code
3.5 Documentation
3.5.1 Writing documentation
3.5.2 Generate and view documents
3.5.3 Hosted documents
3.5.4 Document properties
3.5.5 Documented testing
3.6 Benchmark
3.6.1 Built-in micro-benchmark tools
3.6.2 Benchmarking on the stable version of Rust
3.7 Writing and testing software packages - logic gate simulator
3.8 CI integration testing and Travis CI
3.9 Summary
Chapter 4 Types, Generics, and Traits
4.1 Type systems and their importance
4.2 Generics
4.2.1 Creating generics#
4.2.2 Generic implementation
4.2.3 Generic applications
4.3 Using features to abstract behavior
4.3.1 Characteristics
4.3.2 Various forms of features
4.4 Using package generic features—feature intervals
4.4.1 Characteristic intervals on types
4.4.2 Characteristic intervals on generic functions and impl code blocks
4.4.3 Use " " feature combination to form an interval
4.4.4 Feature interval and impl feature syntax
4.5 Introduction to standard library features
4.6 Using trait objects to achieve true polymorphism
4.6.1 Distribution
4.6.2 Characteristic objects
4.7 Summary
Chapter 5 Memory Management and Security
5.1 Programs and Memory
5.2 How programs use memory
5.3 Memory management and classification
5.4 Introduction to memory allocation
5.4.1 Stack
5.4.2 Heap
5.5 Defects in Memory Management
5.6 Memory safety
5.7 Three principles of memory safety
5.7.1 Ownership
5.7.2 Reusing types through traits
5.7.3 Borrowing
5.7.4 Method types based on borrowing rules
5.7.5 Life cycle
5.8 Pointer types in Rust
5.8.1 References—safe pointers
5.8.2 Raw pointers
5.8.3 Smart pointers
5.8.4 Reference-counted smart pointers
5.8.5 Application of internal variability
5.9 Summary
Chapter 6 Exception Handling
6.1 Introduction to exception handling
6.2 Recoverable exceptions
6.2.1 Option
6.2.2 Result
6.3 Option/Result combination
6.3.1 Common combinators
6.3.2 Combiner application
6.3.3 Conversion between Option and Result types
6.4 Early return and operator "?"
6.5 Unrecoverable exceptions
6.6 Custom errors and Error characteristics
6.7 Summary
Chapter 7 Advanced Concepts
7.1 Introduction to type systems
7.1.1 Code blocks and expressions
7.1.2 let statement
7.1.3 Loops as expressions
7.1.4 Type clarity and symbol distinction in numeric types
7.1.5 Type inference
7.1.6 Type aliases
7.2 String
7.2.1 String containing ownership - String
7.2.2 Borrowing string——&str
7.2.3 String slicing and chunking
7.2.4 Using strings in functions
7.2.5 String concatenation
7.2.6 Application scenarios of &str and String
7.3 Global values
7.3.1 Constants
7.3.2 Static values
7.3.3 Compile-time function——const fn
7.3.4 Dynamic static values through lazy_static macro
7.4 Iterators
7.5 Advanced types
7.5.1 Indefinite length types
7.5.2 Function types
7.5.3 never type “!” and function dispatch
7.5.4 Union
7.5.5 Cow
7.6 Advanced features
7.6.1 Sized and ?Sized
7.6.2 Borrow and AsRef
7.6.3 ToBoned
7.6.4 From and Into
7.6.5 Characteristic objects and object security
7.6.6 General function call syntax
7.6.7 Feature rules
7.7 Advanced closures
7.7.1 Fn closure
7.7.2 FnMut closure
7.7.3 FnOnce closure
7.8 Constants in structures, enumerations, and traits
7.9 Modules, paths and imports
7.9.1 Import
7.9.2 Import again
7.9.3 Privacy
7.10 Advanced Matching Patterns and Guards
7.10.1 Match Guard
7.10.2 Advanced let construction
7.11 Casting
7.12 Types and Memory
7.12.1 Memory alignment
7.12.2 std::mem module
7.13 Serialization and deserialization using serde
7.14 Summary
Chapter 8 Concurrency
8.1 Program execution model
8.2 Concurrency
8.2.1 Concurrent methods
8.2.2 Defects
8.3 Concurrency in Rust
8.3.1 Thread basics
8.3.2 Custom threads
8.3.3 Accessing data in threads
8.4 Thread concurrency model
8.4.1 State sharing model
8.4.2 Mutual exclusion
8.4.3 Shared mutability through Arc and Mutex
8.4.4 Communication via messaging
8.5 Thread safety in Rust
8.5.1 What is thread safety?
8.5.2 Characteristics of thread safety
8.5.3 Send
8.5.4 Sync
8.6 Implementing concurrency using the actor model
8.7 Other libraries
8.8 Summary
Chapter 9 Macros and Metaprogramming
9.1 What is metaprogramming?
9.2 Application scenarios of Rust macros
9.3 Macros and their types in Rust
9.4 Create macros using macro_rules!
9.5 Built-in macros in the standard library
9.6 macro_rules! Macro tag type
9.7 Repetition in macros
9.8 Advanced application of macros - writing DSL for HashMap initialization
9.9 Macro use cases - writing tests
9.10 Exercise
9.11 Process macros
9.12 Derived macros
9.13 High level macros
9.14 Commonly used process macro software packages
9.15 Summary
Chapter 10 Unsafe Rust and External Function Interfaces
10.1 Safety and Unsafe
10.1.1 Unsafe functions and code blocks
10.1.2 Unsafe characteristics and implementations
10.2 Calling C code in Rust
10.3 Calling Rust code through C language
10.4 Using external C/C libraries in Rust
10.5 Constructing native Python extensions using PyO3
10.6 Creating native extensions for Node.js in Rust
10.7 Summary
Chapter 11 Log
11.1 Logging and its importance
11.2 Requirements for a logging framework
11.3 Logging framework and its features
11.4 Logging method
11.4.1 Unstructured logging
11.4.2 Structured logging
11.5 Logging in Rust
11.5.1 log—Logging for Rust
11.5.2 env_logger
11.5.3 log4rs
11.5.4 Using slog for structured logging
11.6 Summary
Chapter 12 Rust and Network Programming
12.1 Introduction to Network Programming
12.2 Synchronous network I/O
12.3 Asynchronous network I/O
12.3.1 Asynchronous abstractions in Rust
12.3.2 Building an asynchronous Redis server
12.4 Summary
Chapter 13 Building Web Applications with Rust
13.1 Web applications in Rust
13.2 Using hyper for HTTP communication
13.2.1 Hyper server-side API builds a short URL service
13.2.2 Hyper as a client - building a URL short client
13.2.3 Web framework
13.3 Actix-web basic knowledge
13.4 Building a bookmark API using actix-web
13.5 Summary
Chapter 14 Rust and WebAssembly
14.1 The Importance of Data Durability
14.2 SQLite
14.3 PostgreSQL
14.4 r2d2 connection pool
14.5 Postgres and diesel ORM
14.6 Summary
Chapter 15 Rust and WebAssembly
15.1 What is WebAssmbly
15.2 Design Goals of WebAssembly
15.3 Getting started with WebAssembly
15.3.1 Try online
15.3.2 Methods to generate WebAssembly
15.4 Rust and WebAssembly
15.4.1 wasm-bindgen
15.4.2 Other WebAssembly projects
15.5 Summary
Chapter 16 Rust and Desktop Applications
16.1 Introduction to GUI development
16.2 GTK framework
16.3 Build a news desktop application through gtk-rs
16.4 Exercises
16.5 Other emerging UI frameworks
16.6 Summary
Chapter 17 Debugging
17.1 Introduction to debugging
17.1.1 Debugger Basics
17.1.2 Prerequisites for commissioning
17.1.3 Configuring GDB
17.1.4 An example program—buggie
17.1.5 GDB basics
17.1.6 Integrating GDB in Visual Studio Code
17.2 Introduction to the rr debugger
17.3 Summary