Origin
This series of articles was not planned. Yesterday with VP and seven to discuss the problem of no GC management memory.
To discuss the learning curve that does not manage memory in the case of GC, seven of elder brothers think that learning curve is not steep but use curve is steep. Admittedly, if only malloc and free, it is really easy to learn to use difficult. After the C + + reference to the new and delete, the learning curve is also considered peaceful, because there are auto_ptr, automatic reference count, right value reference and Std::move and so on a lot to learn slowly add in. In the case of multithreading, there will be more complex problems.
But this is better than rust language, because the big deal is memory leak, at least it can be compiled. In the rust language, the initial small steep slope, if not, may even be compiled.
This is the design principle of rust, do not want to have a heavier runtime, through the compilation of diligence to reduce run-time trouble. A brief introduction to rust language
Rust is a language that Mozilla has launched, hoping to give full play to its hardware capabilities in the multi-core era. Rust can be a source of concern, initially because one of its designers, Brendan Eich, is the father of the famous JavaScript language. No matter how much the programmer is critical of JavaScript, the success of JavaScript is a myth that is hard to repeat. JavaScript, in the era of OOP, combines self's prototype chain with scheme's functional programming ideas to show the Brendan Eich's skill. However, the initial version of JavaScript development time is too short to give Brendan Eich the opportunity to play their own abilities. Later, with the history of the baggage, it is not to change how to change the situation. After JavaScript became ECMA script, the 4.0 version of the discarded, is the performance of this setback.
Rust is a new language, and three of the main creative people can open their hands and feet to play. They did it, and the grammar changed. For example, rust initially supported go-like lightweight multitasking support, which was then chopped off, and now is the traditional thread that sends messages back and forth like go.
However, in fact, the earliest founder of Rust is Graydon Hoare. He started developing rust in 2006, and the earliest compilers were ocaml. In 2009, Hoare continued to develop rust with the support of Mozilla company.
May 16, 2015, Rust officially released the 1.0 version. Hoare has left Mozilla, but Rust continues to develop with Mozilla's support.
Servo, developed in rust language, is a browser engine similar to WebKit.
An important feature of the rust language is that it does not have a heavy running time, so there is no runtime facility like GC. The downside is that we need to learn to design without the GC, that's what we want to do. The advantage is that lightweight, can be used for system programming, code can run on bare metal, not a set of run-time environment. In this way, in theory, rust compiled code can be as light as C and C + +, and can be used for embedded system-level software development, such as IoT devices. In contrast to the go language, which has a run-time, write application is likely to win, but when writing system-level software, this runtime environment may not be the most ideal infrastructure.
For example, go and rust also write JNI code for Java. Rust can be used as a C + + use, but go will introduce a very interesting problem, Java has gc,go also have GC, such JNI code how to deal with the problem of referencing objects.
Rust and C + + can be basically seamless to call, anyway, essentially the idea is similar.
Rust supports functional thinking and supports macros similar to the function language that C + + does not have. Rust support generics, support traits, this is what C + + programmers like, and Java is just a syntactic sugar way beyond imagination.
Often compared with rust and go, there are the swift language of Apple and the D language of digital Mars. Their common feature is the high productivity of grammar and the efficiency of full compilation.
Well, let's start with a hello,world program to say hello to the rust language.
First go to the official website download Rust Package: https://www.rust-lang.org
On the Mac, just use the brew install rust.
IDE, I'm using visual Studio Code and you're free to choose.
fn Main () {let
s = "hello,rust!";
println! ("{}", s);
}
Call RUSTC, or through cargo, you can compile and run.
You can create a project by cargo init and then use cargo build or cargo run.
println! is an output of the macro, do not forget the back of the hint that it is a macro exclamation yo. Rust is a language with no GC
Well, less gossip, we're starting to face the first hurdle of learning a new language like C + + without a GC, managing memory.
Rust like C + +, storage space is both heap and stack. Objects are placed on the stack, can be copied, and can be automatically destroyed as the stack is dropped. Before c introduces malloc, it is either statically allocated or distributed on the stack, and there is no complex memory leak problem on the heap. In the Java-represented language, the default is to allocate objects on the heap, so the GC is needed to implement the management on the heap.
We also use containers as an example to illustrate this problem:
Let V = vec! [1,2,3];
println! ("{}", v[0]);
vec! macros are used to initialize a vector container. Let is called a binding, binding a value on a variable.
The V variable itself is allocated inside the stack, while the three elements in V are allocated inside the heap. When the function exits, the scope of V ends and the elements in the heap that it references are automatically recycled. Sounds good, at least pairing up with malloc and free.
Here's the question, what happens if you want to bind the value of V to another variable, v2.
For systems with GC, this is not a problem, and both V and V2 refer to references in the same heap and are eventually recycled by GC.
For systems that do not have a GC, there are three options:
1. Copy: Duplicate the data in the heap and bind to the new variable. The assignment of objects on a stack in C + + is done in this way, calling a copy constructor to take care of the replication.
2. Move: The Std::move semantics introduced by c++11 is to do this and transfer ownership. The value on the heap is referenced by V2, and the reference to V is invalid.
3. Reference count: Use automatic reference counting to release the data on the heap when the reference count is 0. c+, +11 and Boost's shared_ptr is the idea. Rust default is mobile semantics
C + + is the default for replication semantics, and rust is the default for mobile semantics.
In rust, what happens if you reference an element in an object that has moved the control. The answer is, it's not even a compilation.
If that's what we write:
Let V = vec! [1,2,3];
Let v2 = V;
println! ("{}", v[0]);
The compiler will give us this error, direct compilation failed.
In fact, the compiler gives the wrong message is pretty friendly ah.
ERROR[E0382]: Use of moved value: ' V '
--> src/main.rs:7:16
|
6 | Let v2 = V;
| --value moved here
7 | println! ("{}", v[0]);
| ^ value used here
<std macros>:2:27:2:58 note:in This expansion of format_args!
<STD macros>:3:1: 3:54 note:in This expansion of print! (defined in <STD macros>)
Src/main.rs:7:2:7:22 note:in This expansion of println! (defined in <STD macros>)
|
= Note:move occurs because ' V ' has type ' std::vec::vec<i32> ', which does not implement the ' Copy ' trait
Compilation failure tells us that unless copy trait is implemented, the default for a type is mobile semantics.
OK, you know, the default move is to move. Be careful, let's take a look at the following example.
We're just trying to tune a harmless function:
fn Take (v:vec<i32>) {
println! (" I did nothing on v! ");
}
fn Main () {let
v = vec![ 1,2,3];
Take (v);
println! ("{}", v[0]);
}
What is the result of the compilation.
ERROR[E0382]: Use of moved value: ' V '
--> src/main.rs:8:16
|
7 | Take (v);
| -Value moved here
8 | println! ("{}", v[0]);
| ^ value used here
<std macros>:2:27:2:58 note:in This expansion of format_args!
<STD macros>:3:1: 3:54 note:in This expansion of print! (defined in <STD macros>)
Src/main.rs:8:2:8:22 note:in This expansion of println! (defined in <STD macros>)
|
= Note:move occurs because ' V ' has type ' std::vec::vec<i32> ', which does not implement the ' Copy ' trait
As with the above binding to another variable, V's ownership has been taken away by other variables.
What can be done about it.
In fact, there is no mystery. Just as a language with a GC usually references a weak reference, what is needed is a way of not causing ownership transfer, or a reference to the rust language. With a symbol similar to C + +, adding & to the type indicates a reference.
Rewritten as a reference to the following way, you can compile it correctly:
FN Take (V: &Vec<i32>) {
println! (" I did nothing on v! ");
}
fn Main () {let
v = vec![ 1,2,3];
Take (&v);
println! ("{}", v[0]);
}
In rust, there is a new name called "Borrow"-borrowed in the way of quoting. Only works, regardless of the life cycle of destruction. It's very similar to a weak quote, and you should be able to understand it well. Rust is the language of invariance priority
The rust language is designed to be a language that is more considered for thread safety. And what kind of data in multithreading under the safest. The answer is unchanging data. The concurrent Masterpiece Java concurrency in Practice, Java World, tells you how to do state closures before you talk about how to use a rich multitasking tool.
Rust is also fully aware of this in its design. Why is it that other languages are called variable assignments, whereas in rust it is a variable binding? The important reason is that this binding is invariant by default.
For example, we define a variable binding by default, and then operate on this variable, which results in a compilation but.
Let's try one of the simplest examples:
let a = 1;
A = a + 2;
Compile the following error:
--> Src\main.rs:10:5
|
9 | let a = 1;
| -Assignment to ' a '
| A = a + 2;
| ^^ ^^ ^^ ^^ ^ re-assignment of immutable variable
So what if you want to define a binding that can be changed? Of course, but in the rust, this is not the default behavior, you need to add mut keyword to explain, we will rewrite the above example, after let add Mut:
Let mut a = 1;
A = a + 2;
println! ("{}", a);
This is where rust differs from many other languages, such as C + + and Java, which are variable by default and immutable with const and final. And rust in turn, unchanged is normal, variable to add Mut.
For example, I want to add a value to vector V:
Let V = vec! [1,2,3];
V.push (4);
Will cause the compilation to fail:
--> Src\main.rs:7:5
|
6 | Let V = vec! [1,2,3];
| -use ' Mut v ' mutable
7 | V.push (4);
| ^ Cannot borrow mutably
Also need to change into mut to push:
Let Mut v = vec! [1,2,3];
V.push (4);
With the knowledge that default is invariant, we look back at the statement that caused the transfer of ownership:
Let V = vec! [1,2,3];
Let v2 = V;
It is a bit inhuman, because V and v2 are not mut, are constant AH.
Why are two constants still not able to share the same heap data? The answer is simple, because the life cycle of two invariants may be inconsistent, and the default variable binding is not a function of reference counting, which is always bound to the lifecycle of a variable.
Also, reference defaults are invariant, and you need to add mut to change the value of the reference.
For example, the following is still compiled:
FN Take (V: &Vec<i32>) {
V.push (4);
}
Report the same mistake:
Error:cannot Borrow immutable borrowed content ' *v ' as mutable--> src\main.rs:3:5
|
3 | V.push (4);
| ^
We also need to use mut to decorate it, and change the place where & appears to &mut
FN Take (V: &mut vec<i32>) {
V.push (4);
}
fn Main () {let
mut v = vec![ 1,2,3];
Take (&mut v);
println! ("{}", v[3]);
}
The output turned out to be 4, and it was finally done. Multiple borrowing
Let's continue to think about a problem where there are only one borrower, and only one reference, then how many references will be.
Like this is not possible.
Let Mut v = vec! [1,2,3];
Take (&mut v);
println! ("{}", v[3]);
Let v2 = &v;
Let V3 = &v;
Yes, these are OK.
So there is no change, but also variable, can it.
Let v2 = &v;
Let V3 = &v;
As a result, there are no more errors:
ERROR[E0502]: Cannot borrow ' V ' as mutable because it is also borrowed as immutable
--> src\main.rs:12:23
>10 | Let v2 = &v;
| -Immutable borrow occurs
| Let V3 = &v;
| Let Mut v4 = &mut V;
| ^ mutable borrow occurs
|}
| -Immutable borrow ends here
This is another rust for thread safety, and if everyone is a read-only reference, there can be any number of them. But as long as one is mutable, I'm sorry, that's the only one that can be referenced. Violates this rule, is not the run-time problem, but compiles cannot pass.
So in a piece of code, the business logic is more than a mutable reference to manipulate it?
The solution is a time-sharing operation. As long as it is not a reference to a variable at the same time, we can do it by scope, such as:
{Let
v2 = &v;
Let V3 = &v;
}
{Let
mut v4 = &mut v
}
Only two pairs of parentheses are added, and there is no clear effect on the order. However, because of staggered scope overlap, it is possible to compile the pass smoothly, but also to avoid the risk.
However, it is important to note that a long scope reference, which wants to refer to a life cycle that is shorter than its object, is compiled.
For example, we define the object in a large scope, the reference in the child scope, and we, in turn, define the reference in the large definition field, referencing the object in the child scope, like this:
Let vv: &mut vec<i32>;
{Let
vv2 = vec! [4,5,6];
VV = &mut vv2;
}
It looks good, but it does not:
--> src\main.rs:21:19
|
| VV = &mut vv2;
| ^^^
|
Note:reference must is valid for the "block suffix following statement 5 at 18:26 ...
--> src\main.rs:18:27
|
| Let vv: &mut vec<i32>;
| ^
Note: ... but borrowed value is only valid for the block suffix following statement 0 at 20:30-->
. rs:20:31
|
| Let Vv2 = vec! [4,5,6];
| ^
Summary rust There is no GC-rust object, the default is move semantics. Replication semantics are also supported in special cases. But whether it's moving or copying, variables can't share data on the same heap. To share the object's data, you need to borrow it by reference. There can be only one mutable reference in the same scope. Multiple invariant reference shares are supported when there are no mutable references. Rust variable bindings are invariant by default, and variable needs to be added mut keyword descriptions. The same is true for references. A reference to a larger scope cannot refer to an object in a small scope that lives shorter than it can.