Vector of Rust learning summary

        Those who have used C++ believe that they are familiar with the concept of vector. In C++, we call vector a container, which can store various types of objects like a container. Rust also has the concept of vector. In Rust, vector allows us to store multiple values ​​in a single data structure. All values ​​​​are arranged next to each other in memory. The storage form in memory is the same as that of arrays. Vector can only store the same value. type of value. But with another structure (enumeration), we can use vector to store different types of values. Of course, this is a layer of wrapping. Let's talk about the detailed usage of Rust's vector.

One: Create an instance of vector

fn main() {
    let vec_ins:Vec<u32> = Vec::new();
}

        Above we created a vector instance through the new function of Vec, and specified that the value stored in it is of u32 type, so can we not specify the value type when we create it with new.

        This is not allowed. If you don't specify a type when you create it with new, then because you haven't inserted any values ​​into this vector, Rust doesn't know what type of element we want to store. this point is very important. vector is implemented using generics, and Chapter 10 covers how to use them with your own types. Now, we know that  Vec it is a type provided by the standard library that can hold any type, and when  Vec it holds a specific type, that type is in angle brackets.        

         So if I pass in the elements of the vector while creating, does it mean that the specified type does not need to be displayed.

fn main() {
    let vec_ins = vec![1, 2, 3];
    println!("vec is {:?}",vec_ins); 
}

operation result:

PS F:\skillup\rust\hello_world\greeting> cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\greeting.exe`
vec is [1, 2, 3]

When the vector is created, the elements are passed in, and Rust can infer the stored type based on the inserted elements. It's more common to create one with an initial value  Vec, and Rust provides  vec! macros for convenience. This macro will create a new Vec。

Two: vector element insertion

We have created a vector, so how to insert elements into the vector.

fn main() {
    let mut vec_ins:Vec<char> = Vec::new();
    vec_ins.push('f');
    vec_ins.push('t');
    vec_ins.push('z');
    println!("vec is {:?}",vec_ins); 
}

operation result:

PS F:\skillup\rust\hello_world\greeting> cargo run
   Compiling greeting v0.1.0 (F:\skillup\rust\hello_world\greeting)
    Finished dev [unoptimized + debuginfo] target(s) in 1.19s
     Running `target\debug\greeting.exe`
vec is ['f', 't', 'z']

Rust can use  push methods to insert elements into vector

Three: read the elements of vector

In Rust, we have two ways to get the value of the elements in the vector, one is through the index, and the other is through the get method. Let's look at a concrete example

fn main() {
    let mut vec_ins:Vec<char> = Vec::new();
    vec_ins.push('f');
    vec_ins.push('t');
    vec_ins.push('z');
    println!("vec is {:?}",vec_ins); 

    let second_ele= vec_ins[1];
    println!("the second element is {}",second_ele); 

    match vec_ins.get(1) {
        Some(second_ele) => println!("The second element is {}", second_ele),
        None => println!("There is no second element."),
    }
}

operation result:

S F:\skillup\rust\hello_world\greeting> cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\greeting.exe`
vec is ['f', 't', 'z']
the second element is t
The second element is t

        The reason Rust has two ways of referring to elements is so that the program can choose how to handle the case when the indexed value has no corresponding value in the vector.

        Rust will panic when referencing an element that doesn't exist. This method is more suitable for situations where the program considers that trying to access an element past the end of the vector is a fatal error, and should crash the program. For example, the vector has 5 elements in total, and you use the subscript to get the 6th element, which is an out-of-bounds access. At this time, the program panics. When the method is  get passed an index outside the array, it will not panic but return  None. Consider using it when occasional out-of-vector access is normal. Then your code can have logic that handles  Some(&element) the OR  None . For example, the index might come from a number entered by the user. If they accidentally enter a number that is too large and the program gets  None the value, you can tell the user the current number of vector elements and ask them to enter a valid value. This is much friendlier than crashing the program because of a typo!

        Once the program has acquired a valid reference, the borrow checker will enforce the ownership and borrowing rules to ensure that this and any other references to the vector's contents remain valid. The rule that mutable and immutable references cannot coexist in the same scope. This doesn't work when we get an immutable reference to the first element of the vector and try to add an element to the end of the vector:

fn main() {
    let mut v = vec![1, 2, 3, 4, 5];

    let first = &v[0];

    v.push(6);

    println!("The first element is: {}", first);
}

         Why does the reference to the first element care about changing the end of the vector? The reason this cannot be done is due to the way vectors work: when adding new elements at the end of a vector, it may be required to allocate new memory and copy the old elements to in the new space. At this point, the reference to the first element points to the freed memory. Borrowing rules prevent the program from getting into this situation.

Four: element traversal of vector

 We can use for loop to traverse vector elements

fn main() {
    let mut vec_ins:Vec<char> = Vec::new();
    vec_ins.push('f');
    vec_ins.push('t');
    vec_ins.push('z');
    println!("vec is {:?}",vec_ins); 

    for i in &vec_ins{
        println!("the element is {}",i);
    }
}

operation result:

vec is ['f', 't', 'z']
the element is f
the element is t
the element is z

So can we change the value of the vector element while traversing?

fn main() {
    let mut vec_ins:Vec<char> = Vec::new();
    vec_ins.push('f');
    vec_ins.push('t');
    vec_ins.push('z');
    println!("vec is {:?}",vec_ins); 

    for i in &mut vec_ins{
        *i = 'x'
    }
    println!("the element is {:?}",vec_ins);
}

operation result:

vec is ['f', 't', 'z']
the element is ['x', 'x', 'x']

In order to modify the value pointed to by a mutable reference,   the value in = the dereference operator ( *) must be obtained  before using the operatori

Five: vector uses enumeration to store different types of values

        As mentioned above, vector can only store values ​​of the same type. If there is this limitation, the vector will be almost the same as the array function. What is the height of Tao and the height of magic? Fortunately, vector is absolutely capable of storing different types of values, but with the help of another structure, the members of the enumeration are all defined as the same enumeration type, so when When we need to store different types of values ​​in the vector, we can define and use an enumeration!

#[derive(Debug)]
enum Student {
    Age(i32),
    Sex(char),
    Name(String),
}
fn main() {
    let student = vec![
        Student::Age(18),
        Student::Sex('男'),
        Student::Name(String::from("ftz"))
    ];
    println!("The student is {:?}",student);
}

   Above we use enumeration to store a student's information, including age, gender, and name, corresponding to three different data types.

Six: vector learning summary

        The reason Rust has to know exactly what types are in a vector at compile time is that it needs to know how much memory is needed to store each element. The second benefit is knowing exactly what types are allowed in this vector. If Rust allowed vectors to hold arbitrary types, then values ​​of one or more types could cause errors when operations were performed on vector elements. Using enums plus  match means that Rust can guarantee at compile time that it will always handle all possible cases.

Guess you like

Origin blog.csdn.net/qq_27071221/article/details/129636654