Rust Struct

In Rust, both structs and tuples can bundle several pieces of data of possibly different types into a whole, but each member and the struct itself has a name, so you don't need to remember indices when accessing its members. Tuples are often used for ad-hoc multi-value passing, while structs are used to define commonly used data structures. Each member of a struct is called a "field".

Struct Definition

This is a struct definition:

struct Site {
    domain: String,
    name: String,
    nation: String,
    found: u32
}

Note: If you are used to C/C++, remember that in Rust the struct statement is only used for definition, it cannot declare instances, and it does not need a;symbol, and after each field definition use,to separate.

Struct Instance

Rust is influenced by JavaScript in many places. When instantiating a struct, it uses the JSON object'skey: valuesyntax to achieve the definition:

Example

let example = Site {
    domain: String::from("www.example.com"),
    name: String::from("EXAMPLE"),
    nation: String::from("China"),
    found: 2013
};

If you don't know JSON objects, you can ignore them and just remember the format:

Struct class name {
    field name : field value,
    ...
}

The advantage of this is that it not only makes the program more intuitive, but also does not require entering member values in the order they were defined.

If the struct being instantiated has field names that match existing variable names, you can simplify the writing:

Example


let domain = String::from("www.example.com");
let name = String::from("EXAMPLE");
let example = Site {
    domain,  // equivalent to domain: domain,
    name,    // equivalent to name: name,
    nation: String::from("China"),
    traffic: 2013
};

There is a situation: you want to create a new struct instance where most properties need to be set to the same values as an existing struct, and you only need to change one or two field values. You can use the struct update syntax:

let site = Site {
    domain: String::from("www.example.com"),
    name: String::from("EXAMPLE"),
    ..example
};

Note:..exampleThere cannot be a comma after it. This syntax does not allow copying another struct instance exactly without changes; that is, you must set at least one field's value before referencing another instance's values.

Tuple Struct

There is a simpler way to define and use structs:Tuple Struct。

A tuple struct is a struct that has the form of a tuple.

The difference from a tuple is that it has a name and a fixed type format. It exists to handle simple data that needs a defined type (for frequent use) without being too complex:

struct Color(u8, u8, u8);
struct Point(f64, f64);

let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);

"Color" and "point coordinates" are two commonly used data types, but if you write braces and two names when instantiating, you sacrifice convenience for readability. Rust does not leave this problem unresolved. Tuple struct objects are used in the same way as tuples, accessed via . and indices:

Example

fn main() {
    struct Color(u8, u8, u8);
    struct Point(f64, f64);

    let black = Color(0, 0, 0);
    let origin = Point(0.0, 0.0);

    println!("black = ({}, {}, {})", black.0, black.1, black.2);
    println!("origin = ({}, {})", origin.0, origin.1);
}

Output:

black = (0, 0, 0)
origin = (0, 0)

Struct Ownership

A struct must own the values of its fields, because when the struct goes out of scope, it releases all its fields.

This is why the examples in this chapter use the String type instead of &str.

But this does not mean that reference fields cannot be defined in structs; this requires the "lifetime" mechanism.

But the concept of "lifetime" is hard to explain now, so it can only be explained in later chapters.

Printing Structs

During debugging, displaying a complete struct instance is very useful. But manually writing a format would be very inconvenient. So Rust provides a convenient way to output an entire struct:

Example

#[derive(Debug)]

struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("rect1 is {:?}", rect1);
}

As shown in the first line: be sure to import the debug library#[derive(Debug)], then in the println and print macros you can use{:?}placeholder to output an entire struct:

rect1 is Rectangle { width: 30, height: 50 }

If there are many attributes, you can use another placeholder{:#?} 。

Output:

rect1 is Rectangle {
    width: 30,
    height: 50
}

Struct Methods

Methods are similar to functions, except they are used to operate on struct instances.

If you have studied object-oriented languages, you must know that functions are usually placed in class definitions and use "this" within the function to refer to the instance being operated on.

Rust is not an object-oriented language, as can be seen from the innovation of its ownership mechanism. However, the valuable ideas of object-oriented programming can be implemented in Rust.

The first parameter of a struct method must be &self, and its type does not need to be declared, because self is not a style but a keyword.

Calculate the area of a rectangle:

Example

struct Rectangle {
    width: u32,
    height: u32,
}
   
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    println!("rect1's area is {}", rect1.area());
}

Output:

rect1's area is 1500

Note that when calling a struct method, you do not need to fill in self; this is for convenience.

An example with multiple parameters:

Example

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn wider(&self, rect: &Rectangle) -> bool {
        self.width > rect.width
    }
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    let rect2 = Rectangle { width: 40, height: 20 };

    println!("{}", rect1.wider(&rect2));
}

Output:

false

This program calculates whether rect1 is wider than rect2.


Struct Associated Functions

The reason "struct methods" are not called "struct functions" is that the name "function" is reserved for functions that are in an impl block but do not have a &self parameter.

Such functions do not depend on an instance, but to use them you need to specify which impl block they are in.

The always-usedString::fromfunction is an "associated function".

Example

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn create(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }
}

fn main() {
    let rect = Rectangle::create(30, 50);
    println!("{:?}", rect);
}

Output:

Rectangle { width: 30, height: 50 }

Tip:A struct's impl block can be written multiple times; the effect is equivalent to concatenating their contents!

Unit Struct

A struct can serve as a symbol without any members:

struct UnitStruct;

We call this struct without a body a unit struct.

Other Extensions