[Rust] Custom data type - structure - Rust language foundation 13

1. Structure——struct

A structure is a collection containing a set of elements of different types, which is very similar to the tuple ( ) type introduced earlier Tuple, but unlike a tuple, each element in a tuple only needs to indicate its element type. It needs to be named, and in the structure, it is necessary to clearly name each element and indicate its data type.

But because of this, the structure becomes more flexible, you can directly use the name to access the specified members in the structure, unlike Tuplein access members.

2. Define and initialize a structure

2.1. Define a structure

Here we define a structure of student information:

    struct Student {
    
    
        name: String,
        class: String,
        score:  u64,
        pass:   bool,
    }

2.2. Instantiate the structure when defining the variable

Let's see how to instantiate a structure:

    let lacia = Student {
    
    
        name: String::from("Lacia"),
        class: String::from("10-01"),
        pass: true,
        score:  89,
    };

Create and instantiate a struct variable by binding concrete values ​​to each field in the struct. To create a structure, you need to start with the name of the structure, and then key : valueuse to bind specific values ​​for each field. No order is required during instantiation.

2.3. Initialize the structure with a function

You can also instantiate a structure by constructing a function:

struct Student {
    
    
    name: String,
    class: String,
    score:  u64,
    pass:   bool,
}


fn main() {
    
    


    let mut lacia = Student {
    
    
        name: String::from("Lacia"),
        class: String::from("10-01"),
        pass: true,
        score:  89,
    };


    lacia.class = String::from("10-03");
    println!("Student:\nName: {}\nClass: {}", lacia.name, lacia.class);


    let mut yinoli = build_student(String::from("YiNoLi"), String::from("10-03"), 86);

    println!("Student:\nName: {}\nClass: {}", yinoli.name, yinoli.class);


}

fn build_student(name: String, class: String, score: u64) -> Student {
    
    

    Student {
    
    
        name,
        class,
        score,
        pass: {
    
    
            if score >= 60 {
    
     true }
            else {
    
     false }
        },
    }
}

The last part of a function body can be used to construct an instance of a structure, which can be implicitly returned. If the parameter name in the function is the same as the member name of the structure, the writing method can be simplified, and there is no need to write repeatedly name: namelike this , but can be simply written directly name.

2.4. Use the existing structure to construct other

When constructing a structure, we often encounter this situation. Most of the information of the created structure is the same as before, and the result calculated by the instance in the previous structure will be used to initialize the new structure:

    let mut yinoli = build_student(String::from("YiNoLi"), String::from("10-03"), 86);

    println!("Student:\nName: {}\nClass: {}", yinoli.name, yinoli.class);

    let mut misaka = Student {
    
    
        name: String::from("MiSaKaMiKoTo"),
        class: yinoli.class,
        score: yinoli.score,
        pass:  yinoli.pass,
    };

    println!("Student:\nName: {}\nClass: {}", misaka.name, misaka.class);

RustFor this case, we provide a more convenient initialization method:

    let mut yinoli = build_student(String::from("YiNoLi"), String::from("10-03"), 86);

    println!("Student:\nName: {}\nClass: {}", yinoli.name, yinoli.class);

    let mut misaka = Student {
    
    
        name: String::from("MiSaKaMiKoTo"),
        ..yinoli
    };

    println!("Student:\nName: {}\nClass: {}", misaka.name, misaka.class);

..The syntax indicates that the remaining members of the structure are all initialized with the values ​​​​in the ..subsequent instance. The above two pieces of code have the same effect.

3. Tuple structure

We have already touched the tuple type before Tuple. The author took a screenshot of the example in the previous article, and it should be recalled soon after seeing it.
insert image description here
It seems that tuples are a bit similar to structures, but this is different, as explained at the beginning of this article, so structures support structures defined as tuple types, also known as tuple structures ( ) Tuple Structs. It combines the characteristics of the tuple type, that is, each field in the structure has no specific name, only the type.

Why do you do that? ? It may seem like an unnecessary move but it is of great use. When encountering things that describe two types that are exactly the same, but they cannot be confused, that is, the data types described are the same, but it is by no means an equivalence relationship or a relationship of mutual assignment and operation.

For example, we need to describe the three categories of humans, cars, and trees (only trees), and only care about the information of each of these categories as follows:

  • Human: (1) age, (2) height, (3) weight;
  • Car: (1) maximum horsepower, (2) weight, (3) price;
  • Trees: (1) Age, (2) DBH, (3) Height

It can be seen that these description information can be used in all u32types :

struct Human(u32, u32, u32);
struct Car(u32, u32, u32);
struct Tree(u32, u32, u32);

fn main() {
    
    
    let bizhe = Human(123, 176, 61);
    let porsche_tancan = Car(321, 2100, 86700);
    let wu_tong = Tree(12, 38, 430);

    println!("BiZhe: {} age, {} cm, {} kg", bizhe.0, bizhe.1, bizhe.2);
    println!("Porsche_Tancan: {} hp, {} kg, {} $", porsche_tancan.0, porsche_tancan.1, porsche_tancan.2);
    println!("WuTong: {} age, {} cm, {} cm", wu_tong.0, wu_tong.1, wu_tong.2);
}

The execution results are as follows:

imaginemiracle@im-Linux:tuple$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/tuple`
BiZhe: 123 age, 176 cm, 61 kg
Porsche_Tancan: 321 hp, 2100 kg, 86700 $
WuTong: 12 age, 38 cm, 430 cm

4. Use of structure

4.1. Accessing structure members

Members in the specified structure can be directly accessed by .adding .

    let lacia = Student {
    
    
        name: String::from("Lacia"),
        class: String::from("10-01"),
        pass: true,
        score:  89,
    };


    println!("Student:\nName: {}\nClass: {}", lacia.name, lacia.class);

When you need to modify a member of the structure, you can use the same method.

    let mut lacia = Student {
    
    
        name: String::from("Lacia"),
        class: String::from("10-01"),
        pass: true,
        score:  89,
    };

    lacia.class = String::from("10-03");
    println!("Student:\nName: {}\nClass: {}", lacia.name, lacia.class);

[注]:结构体不允许 mut 只修饰其中某个字段,即不可将结构体与其成员视为不同变量。

4.2. Print structure information

In the above, the fields in the access structure are used to print them out one by one. Is there a way to directly output the structure information at one time? That's right, there are, and the debug printing method of modeRust is provided , let's see how to use it.Debug

#[derive(Debug)]

struct Student {
    
    
    name: String,
    class: String,
    score:  u64,
    pass:   bool,
}


fn main() {
    
    


    let mut lacia = Student {
    
    
        name: String::from("Lacia"),
        class: String::from("10-01"),
        pass: true,
        score:  89,
    };


    lacia.class = String::from("10-03");
    println!("Student:\nName: {}\nClass: {}", lacia.name, lacia.class);


    let mut yinoli = build_student(String::from("YiNoLi"), String::from("10-03"), 86);

    println!("Student:\nName: {}\nClass: {}", yinoli.name, yinoli.class);

    let mut misaka = Student {
    
    
        name: String::from("MiSaKaMiKoTo"),
        ..yinoli
    };

    println!("Student:\nName: {}\nClass: {}", misaka.name, misaka.class);
    println!("Student:\n {:?}", misaka);
}

fn build_student(name: String, class: String, score: u64) -> Student {
    
    

    Student {
    
    
        name,
        class,
        score,
        pass: {
    
    
            if score >= 60 {
    
     true }
            else {
    
     false }
        },
    }
}

This code is basically the same as the previous one, the difference is that it is added at the beginning of the code #[derive(Debug)], indicating that the debugging method is introduced, and a line of output code is also added:

println!("Student:\n {:?}", misaka);

You can see that here is different { }from the previous ones, it is added in the middle :?, this symbol println!tells Debugthe output format that will be used, let's see the output effect:

imaginemiracle@im-Linux:structs$ cargo run
warning: `structs` (bin "structs") generated 3 warnings
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/structs`
Student:
Name: Lacia
Class: 10-03
Student:
Name: YiNoLi
Class: 10-03
Student:
Name: MiSaKaMiKoTo
Class: 10-03
Student:
 Student {
    
     name: "MiSaKaMiKoTo", class: "10-03", score: 86, pass: true }

There is another symbol that also represents Debugthe mode output :#?, that is, the symbols { }in are filled with :#?, see the output effect like this:

imaginemiracle@im-Linux:structs$ cargo run
   Compiling structs v0.1.0 
warning: `structs` (bin "structs") generated 3 warnings
    Finished dev [unoptimized + debuginfo] target(s) in 0.17s
     Running `target/debug/structs`
Student:
Name: Lacia
Class: 10-03
Student:
Name: YiNoLi
Class: 10-03
Student:
Name: MiSaKaMiKoTo
Class: 10-03
Student:
 Student {
    
    
    name: "MiSaKaMiKoTo",
    class: "10-03",
    score: 86,
    pass: true,
}

In the future, we can also implement debugging methods like this ourselves.

4.3. Method Syntax

RustIn , there are two different concepts for methods and functions. Methods and functions are also used fnto modify declarations, and can have parameters and return values. But unlike functions, methods are defined in the context of a specific structure, and the first parameter in the method is to selfrepresent the current structure object.

Let's see an example of defining a method:

struct Student {
    
    
    name: String,
    age: u32,
    height: u32,
    score: u32,
}

impl Student {
    
    
    fn pass(&self) -> bool {
    
    
        if self.score >= 60 {
    
     true }
        else {
    
     false }
    }
}

fn main() {
    
    
    let li_bai = Student {
    
    
        name: String::from("LiBai"),
        age: 18,
        height: 73,
        score: 86,
    };

    if li_bai.pass() {
    
    
        println!("{} is pass.", li_bai.name);
    } else {
    
    
        println!("{} is no pass.", li_bai.name);
    }
}

It can be seen that it is different from the previous function definition method. Here, implthe keyword to modify ( implementation abbreviation), followed by the name of the structure, and then inside implcurly it is basically the same as the normal function definition, but the first function The parameter must be self, and the &selfreference , that is, it does not own selfthe ownership of the . Since the method is defined in the specified structure object, the first variable defaults to the object itself of the structure, and the internal members of the structure can be accessed directly by selfcalling without passing parameters.

Look at the execution effect of the above code:

imaginemiracle@im-Linux:method$ cargo run
warning: `method` (bin "method") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/method`
LiBai is pass.

4.4. Correlation functions

implThe defined structure block has another important function. The defined function allows other parameters or no parameters to be used selfas first parameter. This type of function is called an associated function ( Associated Functions). It should be noted that The definition of is said to be a function not a method, because it does not only act on a struct object. Let's modify the above code to see the difference:

struct Student {
    
    
    name: String,
    age: u32,
    height: u32,
    score: u32,
}

impl Student {
    
    
    fn new(name: String, age: u32, height: u32, score: u32) -> Student {
    
    
        Student {
    
    
            name,
            age,
            height,
            score,
        }
    }
}

fn main() {
    
    
    let li_bai = Student::new(String::from("LiBai"), 18, 73, 86);

    if li_bai.score >= 60 {
    
    
        println!("{} is pass.", li_bai.name);
    } else {
    
    
        println!("{} is no pass.", li_bai.name);
    }
}

Here we have seen that the code has changed from the previous one, especially when calling, here is used ::to call the associated function, and the first parameter of the associated function is no longer self, but other parameters, the associated function's The function is to construct a structure variable and return it. The running effect of the code here is the same as the previous code and will not be shown here.

Each struct can of course have multiple implblocks . Ok~ Seeing this, I believe you have learned how to use structures Rustin , at least the basic usage methods have been mastered.


Boys and Girls! ! !
Are you ready? In the next section, we will have a small exercise to do!

No! I'm not ready yet, let me review the previous one first.
Previous "[Rust] Reference and borrowing, string slice (slice) type (&str) - Rust language foundation 12"

I'm ready, hang かって来い (let the horse come)!
Next "Rust Language Basics 14"


If you think this article is helpful to you, please leave a like v*
Please respect the author, and please indicate the source when reprinting! Thanks for your cooperation~
[Author]: Imagine Miracle
[Copyright]: This work is licensed under the Creative Commons Attribution-Non-Commercial-Share Alike 4.0 International License .
[Link to this article]: https://blog.csdn.net/qq_36393978/article/details/126720509

Guess you like

Origin blog.csdn.net/qq_36393978/article/details/126720509