A programming language is like a small universe, and the grammar concepts in the language are like stars. To the beginner, looking at these grammatical concepts is similar to the confusion that can arise when looking at a scattering of grammatical concepts. Thanks to the fact that programming languages are created by humans, their authors can be found, their source code can be seen, and even some good programming languages have very rich documentation for you to read and study. From this information, we can learn something: why does a language come into being? What problem is it trying to solve? What design philosophy does it follow? A good language is a language with a philosophy in it. It is what it looks like, what it thinks, what it does. Rust is such a philosophically rich programming language. By understanding what design philosophy Rust adheres to, and further understanding its syntactic structure and programming philosophies, you can gain a systematic grasp of the heart of the language without getting lost in its intricate syntactic details. Quote that the world is changing, but not as fast as you might think, so you need to watch carefully.
Quote that the world is changing, but not as fast as you might think, so you need to watch carefully. In July of this year, two events, both small and small, brought Rust into the public eye.
First, on July 4th, Facebook, a social-networking giant, joined 100 other industry giants to announce plans for Libra. The technical features of the project are built on top of blockchain technology and based on Rust implementation.
Building on blockchain technology shows that giants like Facebook think the next financial world is built on blockchain, because blockchain is the future of trust. The Rust language was chosen to build Libra’s blockchain infrastructure because it enables developers to implement this trust. In Trust, We Trust.
Second, on July 18, Microsoft Security Response Center (MSRC) issued a statement saying that we need a more secure system programming language. In the next series of articles, we’ll explore why Microsoft believes Rust is the best choice for the industry right now.
Microsoft’s series of articles seems to be getting more attention than Facebook’s Libra project. Everyone thought that Microsoft was planning to use Rust, but the fact is that Microsoft already uses Rust internally, such as the Azure IoT Edge project, which has been open sourced on GitHub. Another example is Actix-Web, the Web development framework now popular with the Rust community, which was developed by Microsoft engineer Nikolay for use within the Azure platform.
Microsoft Security Response Center is a department responsible for receiving and processing reports of security related vulnerabilities at Microsoft. Back in February, engineers from the division gave a presentation at the BlueHat security conference in Israel called “Microsoft: 70% of security vulnerabilities are memory security issues.” It took about half a year for the department’s official blog to start proclaiming that Rust should be the industry’s best choice. This shows that Microsoft has come to this conclusion after much deliberation.
The speech, in February, sparked a debate on Reddit. One of the leading arguments is that it’s not the programming language that’s the problem, it’s the people who write the code that aren’t good enough. However, no programmer is all-powerful, and it is possible for the Rust compiler to catch errors that are outside the programmer’s experience. Don’t you need seat belts when you’re on the road and there are better drivers? No. We need secure languages like Rust to prevent errors.
These two things are just “visible” to you, and you may be new to Rust, but Rust has already been introduced by many companies. There are Amazon, Google, Facebook, Dropbox and other foreign giants, and there are Alibaba, Toutiao, Zhihu, Bilibili, PingCap, MysiAPE and other companies in China.
So why, exactly, did these pioneers choose Rust? Are they all big fans of new technology? The answer to that question is what this article is trying to tell you.
1.1 The reason for the rise of any new technology is to solve a problem.
Since the birth of the operating system, the major system-level programming languages, from assembly language to C++, have developed for nearly 50 years, but two problems remain:
Memory safe code is hard to write
It’s hard to write thread-safe code
The essence of these two problems is that C/C++ is a type-insecure language, and their weak memory management mechanisms lead to many common vulnerabilities. In fact, there were excellent languages in the 1980s, such as Ada. Ada has many great features: compile-time type checking, GC-free deterministic memory management, built-in secure concurrency model, data contention, system-level hard real-time programming, and more. However, there is a gap between its performance and that of C/C++ at the same time. At that time, computing resources were scarce and performance was the focus. So, everyone is willing to sacrifice security for performance. This is why C/C++ has become so popular.
Fast forward to 2006, and Graydon Hoare (GH for short), a self-described “professional programming language engineer,” began working on a programming language called Rust.
What is a “professional programming language engineer”? In GH’s own words, professional programming language engineers spend their day developing compilers and toolsets for other languages, but have no involvement in the design of the languages themselves. Naturally, GH came up with the idea of developing his own language, Rust.
The name “Rust” contains GH’s expectations for the language. In nature, Fungi called Rust Fungi, which colonize plants, cause disease and are known as “one of the most feared ecological diseases of the century.” The fungus is so hardy that it can produce as many as five different spore types during its life cycle, and the five life forms can transform into each other, which is “super robust” to use the software term. Recall that Rust’s Logo (shown in Figure 1-1) has five circles on it, which also correspond to the five life forms of Rust, suggesting that Rust is also extremely robust. “Rust” also means “Rust,” which implies “bare metal,” and represents Rust’s system-level programming language attributes, with the ability to directly manipulate the underlying hardware. In addition, “Rust” combines “Trust” and “Robust” in word combinations, suggesting “Trust” and “robustness”. “Rust” is therefore a good name. As it turns out, Rust is more than just a good name. GH believes that the future of the Internet will be as much about security and concurrency as it is about performance. The world’s preference for C and C++ design is constantly shifting. There were a lot of great languages that came out in the 1970s and 1980s with a lot of great features, but their memory models were so simple that they weren’t secure enough. For example, Ada’s dynamic memory management, despite being a high-spec security design, has caused major security failures (in the 1990s, the European Space Agency’s Ariane V launch vehicle failed because Ada was overflowing while converting 64-bit floating point numbers to 16-bit unsigned integers).
So GH’s expectations for the language are as follows:
It has to be more secure and crash resistant, especially when working with memory. There is no need for a system such as garbage collection, and you cannot introduce performance burdens for memory security. Rather than a language with one main feature, it should have a broad set of features that are consistent with each other. These features work well together to make the language easier to write, maintain, and debug, allowing programmers to write safer, more efficient code. In short, it is a language that provides high development efficiency, easy to maintain code, performance comparable to C/C++, and security. As a cornerstone of GH’s vision, Rust today is a modern system-level programming language that simultaneously strives for security, concurrency, and performance.
GH really found the essence of the problem – the development of the Internet so far, the performance problem is no longer the bottleneck of its development, the security problem is the “serious disease” that impedes its development. But why should Rust solve this problem?
1.2 Design Philosophy To achieve its goals, Rust follows three design philosophies:
Memory safety
Zero cost abstraction
practical
In other words, all of the grammatical features in the Rust language are designed around these three philosophies, which are the foundation of the consistency of the Rust language.
1.2.1 Memory Security Security is a top priority for Rust to ensure. If security is not guaranteed, then Rust has no meaning. How is the Rust language designed to be secure?
Modern programming languages have already developed into the stage of “program is type proof”, and type system has basically become the standard of all major programming languages, especially those newly emerged in recent years. The type system provides the following benefits:
Allows the compiler to detect meaningless or even invalid code, exposing hidden errors in the program.
You can provide meaningful type information to the compiler to help optimize your code.
It makes the code more readable and makes the developer’s intentions clearer.
Provides a level of high-level abstraction to improve development efficiency.
In general, a language can be said to be a safe language as long as it is type-safe. In simple terms, type safety means that a type system can ensure that a program’s behavior is meaningful and error-free. The type systems of languages like C/C++ are not type safe because they do not constrain meaningless behavior. One of the simplest examples is an array out of bounds that is not checked at all in C/C++, resulting in Behavior that is outside the language specification, known as Undefined Behavior. These undefined behaviors are the hotbeds of vulnerabilities. So, languages like C/C++ are type-unsafe.
If the Rust language wants to keep memory safe, the first thing it needs to do is ensure type safety.
Among programming languages, Ocaml and Haskell are recognized as paragons of type safety, with a type system that is not only a strong “endorsement” of type theory, but also well-tested in practical production environments. So Rust languages borrow their type systems for type safety, especially Haskell, which you’ll see more of in Rust.
However, using Haskell’s type system directly does not solve the memory security problem. The role of the type system is to define the types of values and expressions in a programming language, categorize them, assign them different behaviors, and guide how they interact. Haskell is a purely functional programming language, and its type system is mainly used to carry its “purely functional” idea, which is the embodiment of category theory. For Rust, its type system carries with it the idea of “memory security.” Therefore, it is necessary to have a secure memory management model, which is expressed through the type system, to ensure memory security.
So, what is memory security? In short, there are no memory access errors.
Memory errors occur only when the program accesses undefined memory. In general, memory errors occur in the following situations:
Reference to a null pointer.
Using uninitialized memory.
Use it after release, that is, use dangling pointer.
Buffer overflow, such as array out of bounds.
An illegal release of a pointer that has already been freed or unallocated is called a repeated release.
These cases cause memory errors because they all access undefined memory. To ensure memory security, the Rust language establishes a strict secure memory management model:
Ownership system. Each allocated memory has a pointer to its exclusive ownership. Only when the pointer is destroyed can its corresponding memory be freed.
Borrowing and life cycle. Each variable has its own life cycle, and when the life cycle is exceeded, the variable is automatically released. In the case of borrowing, you can prevent dangling Pointers, which are used after they are released, by marking life cycle parameters for compiler inspection.
The ownership system also includes the RAII mechanism, borrowed from modern C++, which is the cornerstone of Rust’s GC-free but secure memory management.
After establishing the safe memory management model, it can be expressed by the type system. Rust borns the following features from Haskell’s type system:
No Null Pointer
Default immutability
expression
Higher-order functions
Algebraic data type
Pattern matching
The generic
Traits and association types
Local type derivation
For memory security, Rust has the following unique features:
Affine Type, which is used to express the Move semantics in Rust ownership.
Borrowing, life cycle.
With the power of the type system, the Rust compiler can check a type at compile time to see if it meets the safe memory model, and it can detect memory insecurity at compile time, effectively preventing undefined behavior.
Memory security bugs and concurrency security bugs are caused by the same underlying reason: improper access to memory. Similarly, Rust addresses the issue of concurrency security with a powerful type system loaded with ownership. The Rust compiler detects all data race issues in multithreaded concurrent code at compile time through static check analysis.
In addition to security, Rust strives for efficient development and performance.
If the programming language wants to achieve the efficient development, must have certain abstract expression ability. The most representative language for abstract expressive power is Ruby. But Ruby’s ability to express abstractions comes at the expense of performance. The abstraction of Rust, on the other hand, is cost-free, and there is no runtime performance overhead for Rust abstraction, which is all done at compile time. The abstract code in Listing 1-1, which iterates five times, is expanded at compile time into low-level code that is similar to the handwritten assembly code, so there is no runtime performance overhead associated with interpreting this level of abstraction. For a system-level programming language, zero runtime cost is very important. Rust has done just that. The cornerstones of zero-cost abstraction in Rust are generics and traits.
1.2.3 Usefulness How do you evaluate the utility of a programming language? In fact, there is no unified statement, but it can be judged from the following three aspects:
Practical, first of all, must be able to be applied to the development of industrial products, and secondly must be easy to learn and use.
Benefit refers to the ability to produce a positive effect or influence on the industry.
Stability refers to the stability of the language itself. When solving the same problem, there is no random result depending on the user.
So how does Rust perform in all three areas?
Practical Rust is well prepared to develop an industrial-grade product.
To ensure security, Rust introduces a powerful type system and ownership system that not only guarantees memory security, but also concurrency security, without sacrificing performance.
To ensure support for a hard real-time system, Rust takes a look at deterministic destructions, RAII, and smart Pointers from C++ for automating and deterministic memory management without the introduction of GC, and thus without the “world pause” problem. Although borrowed from C++, these items are much cleaner to use than C++.
In order to keep the program robust, Rust takes a fresh look at error handling mechanisms. There are generally three types of anomalies in everyday development: failures, errors, and exceptions. However, in a procedure-oriented language like C, developers can only handle errors through return values, goto and other statements, and there is no unified error handling mechanism. However, although C++ and Java have introduced the exception handling mechanism, they do not provide the syntax that can effectively distinguish the normal logic from the error logic, but only deal with the whole situation uniformly. As a result, developers can only treat all abnormal cases as exceptions, which is not conducive to the development of a robust system. Exception handling also has a high performance overhead.
The Rust language provides specific handling for each of these three types of anomalies, allowing developers to choose on a case-by-case basis.
In the case of failure, you can use the assertion tool.
For errors, Rust provides a hierarchical error handling based on return values, such as Option for handling cases where null values may exist, and Result for handling errors that can be reasonably resolved and propagated.
For exceptions, Rust treats them as problems that cannot be properly resolved and provides a thread panic mechanism that allows a thread to safely exit in the event of an exception.
With this refined design, developers can handle anomalies at a finer granularity, resulting in more robust systems.
In order to integrate well with the existing ecosystem, Rust supports a very convenient and cost-free FFI mechanism, is C-ABI compliant, and divides the Rust language into Safe Rust and Unsafe Rust at the language architecture level. UnsafeTrust deals exclusively with external systems, such as the operating system kernel. The reason for this division is that Rust compiler checks and traces are scoped; it is not possible to check the security status of external language interfaces, so it is up to the developer to ensure security. UnsafeTrust provides unsafekeywords and unsafeblocks, explicitly separating safe code from Unsafe code accessing external interfaces, and making it easier for developers to debug bugs. Safe Rust means that the developer will trust the compiler to be Safe at compile time, while Unsafe Rust means that the compiler will trust the developer to be able to be Safe at compile time.
Where there are people there are bugs. By careful design, Rust leaves the compiler to do what the machine can check and control, and the developer to do what the machine can’t control. Safe Rust guarantees that the compiler maximizes memory security at compile time and prevents undefined behavior. UnsafeRust serves as a reminder to developers that their code might cause undefined behavior, so be careful! People and compilers share the same “security model”, trust each other, and live in harmony with each other to maximize the likelihood of bugs being created by people.
To make it easier for developers to collaborate with each other, Rust offers Cargo, a very handy package manager. Rust code is compiled and distributed in packages, and Cargo provides a number of commands that make it easy for developers to create, build, distribute, and manage their own packages. Cargo also provides a plug-in mechanism for developers to write custom plugins to meet more requirements. Rustfmt and clippy, for example, can be used to automatically format code and find “bad smells” in code, respectively. Rustfix, for example, can even help developers automatically fix broken code based on the compiler’s suggestions. Cargo is also a natural embrace of the open source community and Git, with the ability to publish written packages to the crates.io site with one click for others to use.
To make it easier for developers to learn about Rust, the official team of Rust has made the following efforts:
Separate a dedicated community working group to write the official Rust Book, as well as other documents of varying depth, such as the compiler document, the Nomicon Book, and so on. Rust Bridge, a free community education program, encourages community blogging, and more.
Documents in the Rust language support the Markdown format, so Rust library documents are expressive. The documentation of many third-party packages in the ecosystem has also been improved.
It provides a very useful online Playground tool for developers to learn, use, and share code.
Rust has been bootstrapping for a long time, making it easy for learners to read the source code to understand its inner workings and even contribute.
The core team of Rust has been working to improve Rust, to make Rust more friendly, to reduce the mental burden of beginners and to slow the learning curve. For example, NLL features were introduced to improve the borrowing check system, allowing developers to write more intuitive code.
Although it borrowed a lot from Haskell about the type system, the Rust team deliberately de-academically designed and marketed language features to make the concept of Rust more accessible.
On the basis of type system, it provides the support of mixed programming paradigm, and provides powerful and concise abstract expression ability, which greatly improves the development efficiency of developers. Provide a more rigorous and intelligent compiler. Based on the type system, the compiler can rigorously examine hidden problems in the code. The official Rust team is also working to optimize the compiler’s diagnostic information, making it easier for developers to locate errors and quickly understand why they occur.
Despite the efforts of the official Rust team, there is still a large majority of developers who believe Rust has a steep learning curve. One of the most critical is Rust’s current borrowing check system. This is because Rust is designed to incorporate the features of many languages, and most developers today are skilled in one of them and not familiar with the features of others. C developers are familiar with the underlying memory management, but not necessarily with C++ ‘s RAII mechanism. Even if you are familiar with C++, you are not necessarily familiar with Haskell’s type system; Even if you are familiar with Haskell’s type system, you may not understand the underlying memory management mechanisms. Not to mention developers of object-oriented languages like Java, Ruby, Python, etc., with built-in GC.
To solve this problem, learn about Rust in the following ways:
Keep a beginner’s mind. When confronted with concepts that are difficult to understand in Rust, it is important not to apply the experience of other languages to them, but to start from the design philosophy of Rust to understand the reasons why Rust’s language features are so designed and to find their inherent consistency.
Learn the concept before you practice it. Many developers of traditional languages learning Rust start by writing code, only to stumble and fail to even compile. Code that seems intuitive fails to compile because of borrowing checks. This is because the Rust compiler finds hidden errors in the code you write without you noticing them. So it’s not that Rust has a steep learning curve, it’s that the hands-on approach to coding is problematic.
Think of the compiler as your friend. Don’t ignore the diagnostic information of the Rust compiler, which in most cases makes the cause of the error very clear. This diagnostic information will help you learn Rust and correct your own misperceptions.
As the saying goes, adversity is also an opportunity. Because of Rust’s characteristics, learning Rust is also a process of self-improvement that will help you become a better programmer.
Benefit and Stability The Rust language addresses memory security and concurrency security issues and can greatly improve software quality. Rust was born to provide the industry with a better alternative to C and C++. Because Rust is a security, concurrency, and performance-conscious language, it can be used for embedded systems, operating systems, network services, and other low-level systems, but it goes beyond that. It can also be used to develop upper-level Web applications, game engines, machine learning, and even front-end components based on WebAssembly technology. Because of its high security and performance comparable to that of C/C++, Rust is also being used in new frontier areas such as blockchain technology.
It is clear that Rust has had a very positive impact on the industry. The Rust language has gone into a plateau since the 1.0 release. While new features are constantly being improved and released, the core of Rust remains the same.
In conclusion, Rust is practical, useful, and stable, and there is no doubt about its usefulness.
Since Rust’s 1.0 release in 2015, Rust language has been widely used by companies and in many fields. Each year, the Rust community comes together to develop a roadmap for the future development of Rust. In 2018, the Rust team launched a new Large Edition plan:
Rust 2015, contains semantic versions of Rust 1.0 through 1.30. The goal is to make Rust more stable.
Rust 2018, Rust 1.31 will be the first semantic release of Rust 2018. The goal is to take Rust further into production.
This large version is orthogonal to the semantic version. The point of the larger version is to facilitate the evolution of Rust itself. For example, you might want to introduce a new keyword try into Rust, but if the only dimension is the semantic version, the new keyword could break the existing Rust ecosystem. Therefore, it is necessary to introduce a larger version that will include the try keyword in Rust 2018. By choosing “edition=2018,” the developer accepts this internal change to Rust and accepts the new try keyword. Major releases upgrade only the superficial syntax functionality, and Rust’s core concepts do not change. If you are compiling separate files with Rustc, you will need to add the “– Edition 2018” parameter.
Rust’s compiler makes it easy to manage version compatibility:
Rust 2015 and Rust 2018 are compatible with each other.
The Rust compiler knows how to compile these two versions, just as javac knows how to compile Java 9 and Java 10, and GCC and Clang know how to handle C++ 14 and C++ 17.
You can rely on Rust 2015 libraries in the 2018 version of Rust and vice versa.
Rust 2015 is not frozen.
Also, major releases are likely to be released every three years, so the next release will be in 2021. However, the Rust team reserves the right to make changes to this.
1.3.1 Language Architecture In order to facilitate learning, the author has sorted out the hierarchical structure of Rust language concepts and divided them into four levels. The bottom layer is the Secure Memory Management layer, which is mainly concerned with memory management-related concepts. The penultimate layer is the type system layer, which acts as a bridge between the preceding and the following. The type system layer carries the upper-level ownership system semantics and mixed programming paradigm, giving Rust a high level of abstraction and security. At the same time, the ability to control operations such as underlying code execution, data representation, and memory allocation is retained.
For developers, the type system, the ownership system, and the hybrid programming paradigm are all they need to know. They don’t need to worry about whether the underlying memory is safe, because the compiler and the type system are there to handle it. Under this language architecture, people and compilers share the same “mental model”, which greatly ensures the security and robustness of the system.
Rust, as an open source project, is also a shining jewel of modern open source software.
All the languages that came before Rust were used only for commercial development, but Rust changed that. For the Rust language, the Rust open source community is also part of the language. The Rust language also belongs to the community.
The Rust team is made up of Mozilla and non-Mozilla members, and to date there are over 1,900 contributors to the Rust project. The Rust team is divided into Core and Other Domain Working Groups. For the goals of Rust 2018, the Rust team is divided into the Embedded Working Group, the CLI Working Group, the Network Working Group, and the WebAssembly Working Group, as well as the Ecosystem Working Group and the Community Working Group.
The designs in these areas go through an RFC process first, and for changes that do not need to go through an RFC process, simply submit a Pull Request to the Rust Project Library. All processes are transparent to the community and are reviewed by contributors, with final decision making, of course, revested in the core group and related domain working groups.
The Rust team maintains three distribution branches: Stable, Beta, and Nightly. The stable and beta versions are released every six weeks. A language Feature or library Feature that is marked to burst and Feature Gate can only be used in the development version.
As of July 2018, according to the Pull Request GitHub Octoverse report, Rust ranks 15th in total PR and is on the rise. Judging by the number of active projects, there are 2,604 active projects in Rust.
On the commercial side, Rust is now growing rapidly with heavy business users, including:
Amazon, use Rust as the build tool.
Atlassian, using Rust on the back end.
Dropbox, which uses Rust on both the front and back ends.
Facebook, which rewrote the source control tool using Rust.
Google, for its part, used Rust in the Fuchsia project.
Microsoft, in part, uses Rust on the Azure IoT network.
NPM, which uses Rust on its core services.
RedHat, which uses Rust to create a new storage system.
Reddit, which handles comments using Rust.
Twitter, and use Rust within the build team.
In addition to the companies listed above, there are many others that can be found on the official Friends of Rust page, including Baidu, Samsung, Mozilla, and others. Rust covers areas such as databases, games, cloud computing, security, science, healthcare, and blockchain, with a growing number of related jobs. Rust’s future looks brighter and brighter, and Rust has a lot to offer.
Rust has been around since its inception with platform portability in mind. Typically, the compile phase is divided into a front-end and a back-end, and Rust, as a compiled language, is similarly divided. The Rust compiler is a compilation front end that does lexical parsing, parsing, type checking, generating intermediate code, optimizing independently of the target machine, and more. By using LLVM as the compiler back-end code generation framework, we can take advantage of the compatibility of LLVM with multiple target machines to achieve cross-platform compilation and optimization. As a result, most of the time, users using Rust don’t have to worry about the specific nature of each target machine platform, and can basically write once and run anywhere. Rust also provides assistance in the form of third-party Crate when users need to deal with cross-platform compatibility issues.
Rust source code is partitioned and parsed to generate an AST (Abstract Syntax Tree). Then the AST is further simplified to HIR (High-level IR) in order to make it easier for the compiler to do type checking. HIR is further compiled to Mir (Middle IR), which is an intermediate representation that was introduced in Rust1.12 for the following purposes.
Reduce compilation time. Mir helps with incremental compilation, so when you recompile after modifying your code, the compiler only counts the changed parts, thereby reducing compilation time.
Reduce execution time. MIR can achieve more fine-grained optimizations before LLVM is compiled, because the optimizations that rely solely on LLVM are too coarse and Rust cannot control, and the introduction of MIR adds more optimization room.
More accurate type checking. Mir will help enable more flexible borrowing checks that will improve the experience of Rust.
Finally, Mir is translated to LLVM IR and then compiled by LLVM processing into target machine code that can run on various platforms.
The emergence of Rust may seem accidental, but in fact it was inevitable. It is inevitable that the future Internet will focus on security and high performance. GH saw this, Mozilla saw this, and the two were able to work together to create Rust.
From the day Rust was born in 2006, the goal was clear — to be a modern system-level programming language with security, concurrency, and high performance. To achieve this goal, Rust follows three design philosophies: memory security, zero-cost abstraction, and utility. With the help of a modern type system, Rust gives the language a high level of abstraction, while retaining control over the underlying level. Developers and Rust compilers share the same set of “mental models,” trust each other and collaborate to maximize the security and robustness of the system. Another thing that differentiates Rust from traditional languages is that it treats the open source community as part of the language. Rust itself is a model for open source projects and is well worth studying.
Some people have called Rust “The New C,” and I agree with them. Rust is The language that ushered in a New era. But Rust may not be bursting out of nowhere with a killer app that sets a trend in a particular field, as other languages have. Rust changed the world in a way best described by an ancient poem, “Good rain is the season, and spring is the season. The wind sneaks into the night, moistening things silently.
Rust is not a silver bullet, and it doesn’t strive for perfection. It simply seeks better solutions to problems on top of the old world built by C and C++. ———————————————— Copyright Notice: This article was originally written BY the CSDN blogger “Six o ‘clock in the Morning” under the CC 4.0 BY-SA copyright agreement. Please attach the link to the original source and this notice. The original link: https://blog.csdn.net/weixin_…