Strings and ownership mechanisms in Rust

Article directory

  • 1. string
  • 2. Ownership
    • 2.1 Ownership and Scope
    • 2.2 Operations on ownership
      • 2.2.1 Transfer
      • 2.2.3 Copy
      • 2.2.3 Delivery
    • 2.3 Quotes
      • 2.3.1 Borrowing
      • 2.3.2 Mutable references

1. string

I have learned that Rust only has a few basic data types, but there is no commonly used string, which is String. Today, let’s learn about String;
String in Rust is part of the standard library, that is, std::String, but this String is slightly different from String in other languages, such as:

As you can see, when I want to define a string variable in a way that specifies the string type, the compiler automatically displays the type as &srr instead of String, and I still specify the String type. There is an error; and it is not difficult to see from the prompts given by the compiler that “hello” is defined as a value of type “&str” instead of a string, so let’s first assume that this is an unknown type. , we will deal with it later, first find a way to define our string, and open the official document: https://doc.rust-lang.org/std/string/struct.String.html

You can see that the first example in the official documentation tells us to create a string like this:

But the official did not say why it is defined in this way. Fortunately, the source code of Rust can be clicked in, and executed step by step from String::from:

The only operation performed by from is to call the to_owned function, but it should be noted that the parameter type passed in here is still ” & amp;str”; then the next step of to_owned is to call as_bytes().to_owned (), the last to_owned() calls to_vec;
So now you understand, for this line of code:

 let hello = String::from("Hello, ");

The “Hello,” we pass in will be considered by the compiler as a character array, that is, a string constant, that is, an object that cannot be changed; but what we need is a mutable string rather than a A fixed set of characters, so the compiler expanded this character array into a vector, which turned into a variable character array, which is the string I ultimately want;

In fact, this is similar to C++, except that the underlying string of C++ should be a pointer instead of a vector

In this way, ” & str” can be understood. It is a constant character array, so it is immutable. Search the official documents, and it turns out that there is one, and it also has a nice name, string slice. For specific reference: https://doc.rust-lang.org/std/primitive.str.html

The official documentation also introduces two types of mutual conversion methods:

The remaining uses are some interfaces that come with these two types. Please refer to the official documentation for details, which will not be described in detail here;

2. Ownership

2.1 Ownership and Scope

Many friends started learning Rust because they heard that it is a safer language than C++, so let’s find out, so what is the safety aspect of it? The security lies in the ownership mechanism, which no longer requires developers to manually manage memory like C++.

First, all programs must manage how they use your computer’s memory while they run. Some languages have a garbage collection mechanism that regularly searches for unused memory when the program is running, such as Java; in other languages, programmers must allocate and release memory themselves, such as C/C++. Rust has chosen the third approach: managing memory through an ownership system, which the compiler will check according to a series of rules at compile time. If any of these rules are violated, the program will not compile. While running, no features of the ownership system will slow down the program. Ownership has the following rules:

* Every value in Rust has an owner.
* A value has exactly one owner at any time.
* When the owner (variable) leaves the scope, this value will be discarded.

For example, there is the following piece of code:

fn main() {
    let s1 = "hello";
    {
        let s = "hello s";
    }
    let s2 = "hello";
}

In the above code, s is surrounded by a bracket, then the owner of “hello s” in this bracket is s, and the life cycle of s is only within the bracket range, that is, when s1 appears, s has not appears, s is already dead when s2 appears.

Friends who have experience in C++ will definitely be familiar with this. Isn’t this RAII? In other words, every variable in Rust is a smart pointer.

2.2 Operations on ownership

2.2.1 Transfer

I just mentioned that this ownership mechanism is very similar to C++’s RAII, and variables are also very similar to smart pointers. So is it the same as a smart pointer? Let’s test it:

As you can see from the picture, s1 is defined first, then s2 is defined, and then s1 is passed to s2. If you use s1 again at this time, an error will be reported to remind you that the value of s1 has been transferred, and s1 has been cleared. Because Rust variables will be recycled when they leave the scope, if they are not cleared here, both s1 and s2 will be recycled at the end of the program, so a piece of memory will be recycled twice. Therefore, the = operation in Rust’s mechanism is a transfer rather than a copy.

Seeing this, I still think it is a smart pointer, but it is unique_ptr, QAQ

2.2.3 Copy

= is a transfer rather than a copy, so how to write it if you want to use the copy? For example, the string looks like this:

Just call the clone function.

Then there is a very interesting thing, such as:

i32 type variables do not need to use functions such as clone, = is actually a copy rather than a transfer;
The official explanation is“Types with a known size at compile time, such as integers, are stored entirely on the stack, so copying their actual values is fast. This means that there is no reason to copy the variable y after creating it Invalidate x.” IMHO, isn’t this why all variables that use the heap in Rust use smart pointers. . . . . Of course, it may be that I don’t understand it well enough, but that’s how I feel so far. . . .

2.2.3 Delivery

I mentioned before that “a value has and has only one owner at any time”, so what happens if the value is passed to a function:

Obviously, ownership is lost after being passed as a parameter to a function;
But this will bring about a problem. What if this value is used in more than one function? Return with function return value!
But what if there is more than one parameter? Define a structure for the return value of each function?
This is too troublesome, but fortunately Rust provides references.

2.3 Quote

2.3.1 Borrowing

According to the official description, “A reference is like a pointer because it is an address from which we can access data belonging to other variables stored at that address. Unlike pointers, references are guaranteed to point to a valid value of a specific type.”
It’s easy to understand. Let’s change the code just now:

It works. The modification is that a & amp; is added when the function declares parameters, and an & amp; is also added when passing parameters;

The valid scope of the variable str is the same as the scope of the function parameter, but the data pointed to by the reference is not discarded when str is ceased to be used, because str does not have ownership. When a function takes a reference instead of an actual value as a parameter, there is no need to return a value to relinquish ownership because there was no ownership.

Rust calls the act of creating a reference borrowing. Just like in real life, if a person owns something, you can borrow it from him. When you are done using it, you must return it. You do not own it, so the borrowed value cannot be modified.

2.3.2 Mutable references

Just like immutable variables and mutable variables, references can also become mutable references. Try adding the mut key:

Of course, as written in this code, the prerequisite for a mutable reference is that it itself is mutable.
In addition, due to the three basic rules of Rust, “a value has and has only one owner at any time”, then a mutable variable cannot have multiple references at the same time; from another perspective, for example, using s1 as s After the variable reference is made, then s will no longer be available, and naturally you can no longer create a reference to an unusable variable. If you use it in this way, the compiler will report an error: