Rust 数据类型 之 结构体(Struct)

目录

结构体(Struct)

定义与声明

结构体定义

结构体实例

结构体分类

单元结构体(Unit Struct)

元组结构体(Tuple Struct)

具名结构体(Named Struct)

结构体嵌套

结构体方法

例1:结构体转换为字符串描述

例2:矩形的周长和面积

例3:结构体字段的更新与输出

关联函数

结构体方法与关联函数的区别

参数传递方式的区别

使用方式的区别

结构体的trait

#[derive(Debug)]

例1:

例2:

自定义打印宏 

1. impl fmt::Debug for Student

2. impl fmt::Display for Student

#[derive(PartialEq)]

例1:

例2:

#[derive(Default)]

例1:

例2:

#[derive(Clone)]

其他相关内容

模式匹配

例1:

例2:

结构体大小

1. std::mem::size_of

2. std::mem::size_of_val

本文总结


结构体(Struct)

是一种自定义数据类型,允许将多个相关的值组合在一起,形成一个更复杂的数据结构。结构体被广泛应用于组织和管理数据,具有灵活性和强大的表达能力。

定义与声明

结构体定义

在Rust中,定义和声明结构体的语法如下:

struct Name {  
    field1: Type1,  
    field2: Type2,  
    // ...  
    fieldN: TypeN,  
}

其中,Name是结构体的名称,每个数据名及其对应的数据类型组成一个字段,field1fieldN是结构体的字段名称,Type1TypeN是字段的数据类型

通过关键字 struct 定义,指定结构体名称,结构体内用 field:type, 表示字段名称及数据类型,注意rust语言不能在定义的同时进行赋值,且用逗号分隔各字段,不像c/c++用分号

结构体中可以根据需要定义字段个数,理论上要多少就定义多少;但实际上字段太多,结构体也会变得很占空间,对程序的空间效率是个负担。

结构体实例

如以下代码定义了一个名为Point的结构体,包含x和y两个字段,类型分别为i32和f64:

struct Point {
    x: i32,
    y: f64,
}

定义结构体后,可以像使用其他类型一样使用它。例如,可以声明一个Point类型的变量,并为其字段赋值

let my_point = Point { x: 10, y: 20.0 };

使用结构体时,用成员运算符 my_point.x 来调用对应字段的值:

println!("({},{})", point.x, point.y);    // 输出:(10,20)

可变实例

需要变动字段的值,在声明时需要用 let mut,如:

struct Point {  
    x: i32,  
    y: f64,  
}

fn main() {
	let mut point = Point { x: 10, y: 20.0 }; 
	point.x = 5;
    println!("({},{})", point.x, point.y);  // 输出:(5,20)
}

结构体分类

在Rust中,结构体(Struct)可以按照不同的方式进行分类,以下是一些常见的分类方式:

单元结构体(Unit Struct)

这种结构体没有任何字段,它只是用于表示一个空的类型。这种结构体通常用于作为其他结构体的组成部分或返回类型。例如:

struct UnitStruct;

元组结构体(Tuple Struct)

这种结构体包含一组字段,可以通过元组语法来访问每个字段。元组结构体可以用于表示简单的数据集合,不使用大括号{},而是使用元组的小括号()。例如:

struct TupleStruct(i32, String);

相当字段数据没有名称的结构体,访问时使用索引。如:

struct Point (i32, f64);

fn main() {
	let mut point = Point(10, 20.0); 
	point.0 = 5;
    println!("({},{})", point.0, point.1);  
}

具名结构体(Named Struct)

这种结构体有一个显式的名称,并且包含一组字段。具名结构体可以用于表示复杂的数据结构,例如一个包含多个字段的对象,本文的示例大多数都为具名结构体,用法已在本文开头讲过:

struct MyStruct {  
    field1: i32,  
    field2: String,  
    // ...  
}

除了以上三种常见的结构体类型,Rust还支持其他特殊类型的结构体,例如带有泛型参数的结构体、具名元组结构体(Named Tuple Struct)和结构体路径(Struct Type Alias)等。

需要注意的是,在Rust中,结构体的分类并不是强制性的,也就是说,一个结构体可以包含任意类型的字段,并且可以在任何地方使用。这使得结构体非常灵活,可以用于实现各种复杂的数据结构。

结构体字段的数据类型可以是以下常见的rust数据,甚至可以是函数、引用、指针类型。

  1. 标量类型(Scalar Types):

    • 整数类型(Integer Types):包括有符号整数类型和无符号整数类型。常见的整数类型有 i8i16i32i64i128 表示有符号整数,u8u16u32u64u128 表示无符号整数。此外,还有 isize 和 usize,它们根据平台的位数自动调整大小。
    • 浮点数类型(Floating-Point Number Types):包括 f32 和 f64 两种类型,表示单精度和双精度浮点数。
    • 布尔类型(Boolean Type):只有两个取值,true 和 false
    • 字符类型(Character Type):表示单个 Unicode 字符,通常存储为 4 个字节。
  2. 复合类型(Composite Types):

    • 数组类型(Array Types):由相同类型的元素组成的有限集合。可以通过固定长度或动态长度来定义数组。
    • 切片类型(Slice Types):对一个连续的内存块进行引用,可以看作是动态数组。切片类型提供了访问和操作数据的一种高效方式。
    • 元组类型(Tuple Types):一种将多个不同类型的值组合在一起的数据结构,用圆括号和逗号分隔的元素序列表示。元组可以包含不同类型的元素,例如整数、浮点数、布尔值、字符串等。
    • 结构体类型(Struct Types):一种自定义的数据类型,可以包含多个不同类型的字段。结构体可以通过定义来指定其字段和属性。
    • 枚举类型(Enum Types):表示一个可能取多个值的变量。在 Rust 中,枚举类型使用 enum 关键字定义,每个可能的取值都是一个不同的枚举成员。

结构体嵌套

一个结构体可以包含任意类型的字段,当然也包括结构体。

在以下这个例子中,Address 结构体包含了 street、city 和 state 三个字段,而 Person 结构体则包含了 name、age 和 address 三个字段,其中 address 字段的类型是 Address 结构体。

struct Address {  
    street: String,  
    city: String,  
    state: String,  
}  
  
struct Person {  
    name: String,  
    age: u8,  
    address: Address,  
}

结构体方法

方法(method)是在结构体上定义的功能,可以访问结构体的字段并执行一些操作。使用关键字impl,结构体可以对应一个或多个impl代码块。

例1:结构体转换为字符串描述

struct Student {
    name:String,
    age:u32,
    school:String,
    major:String,
    grade:String,
    state:bool
}
  
impl Student {  
    fn to_string(&self) -> String {  
        format!("Student {{ name: {}, age: {}, school: {}, major: {}, grade: {}, state: {} }}", 
            self.name, self.age, self.school, self.major, self.grade, self.state)  
    }  
} 

fn main() {
    let school = String::from("东南大学");
    let major = String::from("土木工程学院");
    let s = Student{
        name:String::from("杨程"),
        age:22,
        school,
        major,
        grade:String::from("大三"),
        state:true
    };
    println!("{}", s.to_string());
}

输出:

Student { name: 杨程, age: 22, school: 东南大学, major: 土木工程学院, grade: 大三, state: true }

注意:上例中有一个rust结构体的特殊用法,使用同名变量在结构体外为对应字段赋值。

例2:矩形的周长和面积

struct Rectangle {  
    width: f32,  
    height: f32,  
}  
  
impl Rectangle {  
    // 构造函数  
    fn new(width: f32, height: f32) -> Rectangle {  
        Rectangle { width, height }  
    }  
  
    // 计算矩形的面积  
    fn area(&self) -> f32 {  
        self.width * self.height  
    }  
  
    // 计算矩形的周长  
    fn perimeter(&self) -> f32 {  
        (self.width + self.height) * 2.0
    }  
}

impl Rectangle {  
    // 判断矩形是否相等  
    fn is_equal(&self, other: &Rectangle) -> bool {  
        self.width == other.width && self.height == other.height  
    }  
}  
  
fn main() {  
    let rect1 = Rectangle::new(5.0, 6.0);  
    let rect2 = Rectangle::new(5.0, 6.0);  
  
    println!("Rectangle 1 area: {}", rect1.area());  
    println!("Rectangle 1 perimeter: {}", rect1.perimeter());  
  
    println!("Rectangle 2 area: {}", rect2.area());  
    println!("Rectangle 2 perimeter: {}", rect2.perimeter());  
  
    if rect1.is_equal(&rect2) {  
        println!("Rectangles are equal");  
    } else {  
        println!("Rectangles are not equal");  
    }  
}

输出:

Rectangle 1 area: 30
Rectangle 1 perimeter: 22
Rectangle 2 area: 30
Rectangle 2 perimeter: 22
Rectangles are equal 

例3:结构体字段的更新与输出

struct Person {  
    name: String,  
    age: u32,  
}  
  
impl Person {  
    // 这是构造函数,用于创建一个新的 Person 实例  
    fn new(name: String, age: u32) -> Person {  
        Person { name, age }  
    }  
  
    fn say_hello(&self) {  
        println!("Hello, my name is {} and I'm {}.", self.name, self.age);  
    }  
  
    fn update_age(&mut self, new_age: u32) {  
        self.age = new_age;  
    } 

    fn update_name(&mut self, new_name: String) {  
        self.name = new_name;  
    }  
}  
  
fn main() {  
    // 创建一个新的 Person 实例  
    let mut person = Person::new("Tom".to_string(), 5);  
  
    // 调用 say_hello 方法,输出 Person 的信息  
    person.say_hello();  
  
    // 调用 update_age 方法,更新 Person 的年龄  
    person.update_age(3);  
  
    // 再次调用 say_hello 方法,输出更新后的信息  
    person.say_hello(); 

    person.update_age(5);
    person.update_name(String::from("Jerry"));  
    person.say_hello();  
}

输出:

Hello, my name is Tom and I'm 5.
Hello, my name is Tom and I'm 3.
Hello, my name is Jerry and I'm 5.

关联函数

之所以"结构体方法"不叫"结构体函数"是因为"函数"这个名字留给了这种函数:它在 impl 块中却没有 &self 参数。这种函数不依赖实例,但是使用它需要声明是在哪个 impl 块中的,比如上小节例2和例3中的构造函数new()就是关联函数,类似于字符串函数String::new(),String::from("Jerry")。

示例:

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

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

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

输出:

Rectangle { width: 30, height: 50 }
Area: 1500
Area: 1500

结构体方法与关联函数的区别

参数传递方式的区别

结构体方法:结构体方法默认情况下是可变的(mutable),也就是说可以修改结构体的字段。在调用方法时,可以通过引用(&self)或可变引用(&mut self)来传递结构体实例,以便修改其字段。例如:my_struct.my_method(&mut my_struct)。

关联函数:关联函数默认情况下是不可变的(immutable),也就是说无法修改结构体的字段。在调用函数时,只能通过常量引用(&self)来传递结构体实例,因为常量引用是只读的。例如:let my_struct = MyStruct {...}; my_struct.my_function()。

使用方式的区别

结构体方法:结构体方法可以直接在结构体实例上调用,无需显式传递结构体实例。例如:my_struct.my_method()。

关联函数:关联函数需要显式传递结构体实例作为参数。例如:MyStruct::my_function(my_struct)。

结构体的trait

Rust 中的 trait 是一种抽象类型,用于定义泛型行为,trait 可以理解为一种接口。trait 使用关键字 derive 来自动生成实现。通过使用 derive,可以避免手动编写冗长的代码,提高代码的可读性和可维护性。trait 有很多,比如Copy,Clone,Debug,Default,Drop,Hash,Ord,PartialOrd,Send,Sync等等,先挑几种最常用的学一下:

#[derive(Debug)]

在 Rust 语言中用于自动生成一个结构体的 Debug 实现,Debug 是 Rust 标准库中的一个 trait,用于在控制台打印调试信息。

使用 #[derive(Debug)] 属性可以为结构体自动生成一个 Debug 实现,这样在需要打印调试信息时,就可以使用 {:?} 格式化字符串来打印该结构体的内容。例如,在上面的代码中,s 结构体的 Debug 实现已经被自动生成,因此可以使用 println!("{:?}", s) 来打印出结构体 s 的内容。

例1:

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn distance(&self, other: &Point) -> f32 {
        let x_diff = self.x - other.x;
        let y_diff = self.y - other.y;
        ((x_diff * x_diff + y_diff * y_diff) as f32).sqrt()
    }
}

fn main() {
    let p1 = Point { x: 3, y: 0 };
    let p2 = Point { x: 0, y: 4 };
    println!("Distance between {:?} and {:?} is {}.", p1, p2, p1.distance(&p2));
}

输出:

Distance between Point { x: 3, y: 0 } and Point { x: 0, y: 4 } is 5. 

例2:

#[derive(Debug)]
struct Student {
    name: String,
    age: u32,
    school: String,
    major: String,
    grade: String,
    state: bool,
}  

impl Student {
    fn new() -> Student {
        return Student {
            age: 0,
            name: String::new(),
            school: String::from(""),
            major: "".to_string(),
            grade: "".to_string(),
            state: false,
        };
    }
}

fn main() {  
    let mut s = Student::new();
    s.name = String::from("杨程");
    s.age = 22;
    s.school = String::from("东南大学");
    s.major = String::from("土木工程学院");
    s.grade = String::from("大三");
    s.state = true;
    println!("{:?}", s);
}

输出:

Student { name: "杨程", age: 22, school: "东南大学", major: "土木工程学院", grade: "大三", state: true } 

与上一小节的例2对比,输出内容基本一致,就多了String的引号标记。相比自动生成 Debug 实现可以简化代码编写过程,并且可以避免手动实现 Debug 时可能出现的错误。

在本例中,使用宏打印结构体println!("{:?}", s);时,第一行的代码#[derive(Debug)]是必须的,如果去掉就会报错:

error[E0277]: `Student` doesn't implement `Debug`
  --> E:\.rs\struct2.rs:31:22
   |
31 |     println!("{:?}", s);
   |                      ^ `Student` cannot be formatted using `{:?}`
   |
   = help: the trait `Debug` is not implemented for `Student`
   = note: add `#[derive(Debug)]` to `Student` or manually `impl Debug for Student`
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `Student` with `#[derive(Debug)]`
   |
1  + #[derive(Debug)]
2  | struct Student {
   |

error: aborting due to previous error

自定义打印宏 

1. impl fmt::Debug for Student

返回值:fmt::Result; 调用:println!("{:?}", s);

use std::fmt;

struct Student {
    name: String,
    age: u32,
    school: String,
    major: String,
    grade: String,
    state: bool,
}  
  
impl fmt::Debug for Student {  
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f,
               "Student {{ name: {}, age: {}, school: {}, major: {}, grade: {}, state: {} }}",
               self.name, self.age, self.school, self.major, self.grade, self.state)
    }  
}  
  
fn main() {  
    let school = String::from("东南大学");
    let major = String::from("土木工程学院");
    let s = Student {
        name: String::from("杨程"),
        age: 22,
        school,
        major,
        grade: String::from("大三"),
        state: true,
    };  
    println!("{:?}", s);
}

输出:

Student { name: 杨程, age: 22, school: 东南大学, major: 土木工程学院, grade: 大三, state: true }  

2. impl fmt::Display for Student

返回值:fmt::Result; 调用:println!("{}", s); {}里不需要:?

use std::fmt;

struct Point {
    x: i32,
    y: i32,
}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

impl Point {
    fn distance(&self, other: &Point) -> f32 {
        let x_diff = self.x - other.x;
        let y_diff = self.y - other.y;
        ((x_diff * x_diff + y_diff * y_diff) as f32).sqrt()
    }
}

fn main() {
    let p1 = Point { x: 3, y: 0 };
    let p2 = Point { x: 0, y: 4 };
    println!("Distance between {} and {} is {}.", p1, p2, p1.distance(&p2));
}

输出:

Distance between Point(3, 0) and Point(0, 4) is 5.

输出要与使用#[derive(Debug)]时一样,只要修改write宏的第2个参数,如:

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y)
    }
}

#[derive(PartialEq)]

使用#[derive(PartialEq)]为结构体自动实现了PartialEq trait。这使得可以直接使用==运算符比较两个结构体实例的相等性。

例1:

#[derive(PartialEq)]
struct Point {  
    x: i32,  
    y: i32,  
}

fn main() {
	let point1 = Point { x: 10, y: 20 };  
	let point2 = Point { x: 10, y: 20 };  
	if point1 == point2 {  
	    println!("The two points are equal.");  
	} else {  
	    println!("The two points are not equal.");  
	}
}

输出:

The two points are equal.

例2:

#[derive(Debug, PartialEq)]
struct Person {
    name: String,
    age: u32,
}

fn main() {
    let person1 = Person {
        name: String::from("Alice"),
        age: 25,
    };

    let person2 = Person {
        name: String::from("Bob"),
        age: 30,
    };

    let person3 = Person {
        name: String::from("Alice"),
        age: 25,
    };

    println!("Is {:?} equal to {:?}? {}", person1, person2, person1 == person2);
    println!("Is {:?} equal to {:?}? {}", person1, person3, person1 == person3);
}

输出:

Is Person { name: "Alice", age: 25 } equal to Person { name: "Bob", age: 30 }? false
Is Person { name: "Alice", age: 25 } equal to Person { name: "Alice", age: 25 }? true

#[derive(Default)]

调用#[derive(Default)],相当于创建一个默认的结构体实例,每一个字段都是对应数据类型的默认值,无需手动为每个字段设置默认值。

例1:

#[derive(Default,Debug)]
struct Circle {
    radius: f32,
}  

impl Circle {
    fn area(&self) -> f32 {
        let pi = std::f32::consts::PI;
        pi * self.radius * self.radius
    }
}

fn main() {
    let mut c = Circle::default();
    println!("Circular area of {:?} = {}.", c, c.area());
    c.radius = 1.0;
    println!("Circular area of {:?} = {}.", c, c.area());
}

输出:

Circular area of Circle { radius: 0.0 } = 0.
Circular area of Circle { radius: 1.0 } = 3.1415927.

例2:

#[derive(Debug, Default)]
struct Student {  
    name: String,  
    age: u32,  
    school: String,  
    major: String,  
    grade: String,  
    state: bool,  
}  
  
fn main() {  
    let mut s1 = Student::default(); 
    println!("{:?}", s1);
    
    s1.name = String::from("杨程");  
    s1.age = 22;  
    s1.school = String::from("东南大学");  
    s1.major = String::from("土木工程学院");  
    s1.grade = String::from("大三");  
    s1.state = true;  
    println!("{:?}", s1);  
  
    let s2 = Student {  
        age: 23,  
        grade: String::from("大四"),  
        ..s1  //注意这里的结构体更新语法
    };  
    println!("{:?}", s2);  
}

输出:

Student { name: "", age: 0, school: "", major: "", grade: "", state: false }
Student { name: "杨程", age: 22, school: "东南大学", major: "土木工程学院", grade: "大三", state: true }
Student { name: "杨程", age: 23, school: "东南大学", major: "土木工程学院", grade: "大四", state: true }

此例还有一个rust结构体的特殊用法,当结构体大部分字段需要被设置成与现存的另一个结构体的一样,仅需更改其中的一两个字段的值,可以使用结构体更新语法,在现存的结构体名前加上两个连续的句号:“..Struct_Name”。

#[derive(Clone)]

Clone 在复制过程中对所有字段进行逐个复制,包括所有引用类型和原始类型。这意味着每次进行克隆时,都会创建新的数据副本。 

示例:

#[derive(Clone)]
struct Person {  
    name: String,  
    age: i32,  
}  
  
fn main() {  
    let mut person1 = Person { name: String::new(), age: 0 };  
    let mut person2 = person1.clone();

    person1.name = "Alice".to_string();
    person1.age = 22;
    println!("Person 1: {}, {}", person1.name, person1.age);
    println!("Person 2: {}, {}", person2.name, person2.age);
    person2 = person1.clone();
    println!("Person 2: {}, {}", person2.name, person2.age);
}

输出:

Person 1: Alice, 22
Person 2: , 0
Person 2: Alice, 22

其他相关内容

模式匹配

结构体可用 模式匹配(Pattern Matching)来解构和访问其字段。

例1:

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 10, y: 20 };

    match p {
        Point { x, y } => {
            println!("x:{}, y: {}", x, y);
        }
    }
}

例2:

struct Time {  
    hour: i32,  
    minute: i32,  
    second: i32,  
}  
  
fn main() {  
    let t = Time { hour: 10, minute: 30, second: 45 };  
    match t {  
        Time { hour, minute, second } => {  
            print!("The time is {}:", hour);
            println!("{}:{}", minute, second);  
        }  
    }  
}

结构体大小

结构体的大小在C/C++中使用运算符 sizeof 来计算;在Rust语言中,则使用标准库中的一个模块std::mem::中的size_of和size_of_val,它提供了与内存管理相关的函数。

1. std::mem::size_of

用于计算给定类型的大小,不接受任何参数。这个函数返回一个给定类型的大小(以字节为单位)。它是一个泛型函数,可以用于任何类型。

示例:

#![allow(dead_code)]

struct Point {
    x: i32,
    y: i32,
}  
  
struct Person {
    name: String,
    age: i32,
    height: f32,
    is_employed: bool,
}  
  
fn main() {
    let point = Point { x: 10, y: 20 };  
    println!("Size of Point: {}", std::mem::size_of::<Point>());

    let person = Person {
        name: "Hann Yang".to_string(),
        age: 50,
        height: 1.72,
        is_employed: true,
    };  
    println!("Size of Person: {}", std::mem::size_of::<Person>());
}

输出:

Size of Point: 8
Size of Person: 40 

2. std::mem::size_of_val

用于计算给定值的大小,接受一个值作为参数。它用于获取一个值的大小(以字节为单位)。与 size_of 函数不同的是,size_of_val 函数可以用于任何值,而非类型。

示例:

#![allow(dead_code)]

struct Point {
    x: i32,
    y: i32,
}  
  
struct Person {
    name: String,
    age: i32,
    height: f32,
    is_employed: bool,
}  
  
fn main() {
    let point = Point { x: 10, y: 20 };  
    println!("Size of Point: {}", std::mem::size_of_val(&point));

    let person = Person {
        name: "Hann Yang".to_string(),
        age: 50,
        height: 1.72,
        is_employed: true,
    };  
    println!("Size of Person: {}", std::mem::size_of_val(&person));
}

输出:

Size of Point: 8
Size of Person: 40 

注意:在这两个例子中,计算类型大小和值大小的结果都是相同的,因为这里没有涉及到指针或其他复杂的情况。 


本文总结

结构体是Rust中一种重要的数据结构,用于组织不同类型的字段。以下是结构体的重点内容的总结:

  • 结构体定义:使用struct关键字来定义结构体,结构体可以包含多个字段,每个字段可以有不同的类型。
  • 结构体实例:定义一个结构体后,可以使用结构体名称来创建结构体实例,通过.运算符来访问结构体字段。
  • 结构体分类:结构体可以分为三种类型:单元结构体(())、元组结构体(用逗号分隔的多个字段)和具名结构体(有自定义名称的字段)。
  • 结构体嵌套:结构体可以嵌套,用于组织和存储复杂的数据。
  • 结构体方法:结构体可以定义方法,用于在结构体上执行操作。结构体方法与关联函数类似,但只能在结构体上调用。
  • 关联函数:通过impl关键字在结构体上定义关联函数,用于在结构体实例上执行特定操作。关联函数可以是普通函数或方法。
  • 自定义打印宏:使用derive(Debug)]来自动实现fmt::Debug trait,实现自定义的打印输出格式。
  • 其他相关内容:结构体可以通过derive属性来自动实现其他trait,如PartialEq(部分相等性)、Default(默认值)和Clone(克隆)。
  • 结构体大小:在Rust中,结构体的内存大小是固定的,可以在定义时指定大小,也可以使用#[repr(C)]来指定大小和布局。
  • 模式匹配:可以使用模式匹配来访问和匹配结构体的字段,这使得在编写代码时更加灵活和方便。

总的来说,结构体是Rust中非常强大和灵活的数据结构,可以用于组织和操作各种类型的数据。通过使用结构体、方法、关联函数和其他相关特性,可以轻松地实现复杂的数据结构和算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/42578.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jenkins war包 centos启动安装指导

文章目录 步骤1&#xff1a;进入官网&#xff0c;下载到Jenkins的war包1.1 放置在指定位置1.2 放置安装包和创建文件放置路径1.3 检查环境1.4 配置启动命令和结束命令 步骤2&#xff1a; 启动后进入到Jenkins页面2.1 安装插件&#xff0c;例如流水线2.2 依然出现安装插件失败的…

疑问:为什么我的手机不能同时放两张电信卡呢?联通移动可以

很多后台的小伙伴私信我&#xff1a;“为什么我的双卡双待手机不能用两张电信卡呢&#xff1f;”其实我一直在认真的去查证这个问题&#xff0c;因为现在普遍网上的大流量手机卡套餐&#xff0c;电信是主力&#xff0c;如果第一张卡是电信&#xff0c;第二张卡不能使用电信了&a…

公网访问的Linux CentOS本地Web站点搭建指南

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 前言 在web项目中,部署的web站点需要被外部访问,则…

ES6基础知识一:说说var、let、const之间的区别

一、var 在ES5中&#xff0c;顶层对象的属性和全局变量是等价的&#xff0c;用var声明的变量既是全局变量&#xff0c;也是顶层变量 注意&#xff1a;顶层对象&#xff0c;在浏览器环境指的是window对象&#xff0c;在 Node 指的是global对象 var a 10; console.log(window.…

uview2.0使用u-calendar 的formatter属性,在formatter方法里无法访问this的bug,解决办法!!!!

uview 版本2.0.36 文档 使用该文档的案例&#xff0c;在 formatter打印this也会是undefined。 自己写了个demo 父给子传值v-bind传一个函数&#xff0c;然后在这个函数里面打印this&#xff0c;this是子组件的实例&#xff0c;但是不知道为什么formatter里会打印undefined。希…

微服务 云原生:搭建 K8S 集群

为节约时间和成本&#xff0c;仅供学习使用&#xff0c;直接在两台虚拟机上模拟 K8S 集群搭建 踩坑之旅 系统环境&#xff1a;CentOS-7-x86_64-Minimal-2009 镜像&#xff0c;为方便起见&#xff0c;直接在 root 账户下操作&#xff0c;现实情况最好不要这样做。 基础准备 关…

IntelliJ IDEA Copyright添加

IDEA代码文件的版权(copyright)信息配置 1. 快速创建Copyright 版权配置文件 1.1 创建copyright文件 依次点击 File > Settings… > Editor > Copyright > 点击 “” 号或 “Add profile”***&#xff0c;弹出创建 Copyright Profile 操作窗口&#xff0c;在***文…

低代码如何帮助企业数字化转型?

在数字化时代背景下&#xff0c;企业都面临着巨大的数字化转型挑战。为了迎接这一挑战&#xff0c;企业软件开发工具和平台都在不断地创新和进化。其中&#xff0c;低代码开发平台应运而生&#xff0c;并成为了众多企业转型的首选方案。企业为什么都选择低代码开发平台&#xf…

【基于CentOS 7 的NFS服务】

目录 一、概述 二、应用场景 三、安装 四、启动服务 五、目录结构 1.nfs的主配置文件 2.存储配置文件 六、命令解析 1.共享存储管理命令 2.共享目录查看 七、配置 八、客户端访问 1.查看nfs服务器的共享目录 2.挂载 九、实际案例 一、概述 network filesystemt…

【GitOps系列】使用Kustomize和Helm定义应用配置

文章目录 使用 Kustomize 定义应用改造示例应用1.创建基准和多环境目录2.环境差异分析3.为 Base 目录创建通用 Manifest4.为开发环境目录创建差异 Manifest5.为预发布环境创建差异 Manifest6.为生产环境创建差异 Manifest 部署 Kustomize 应用部署到开发环境部署到生产环境 使用…

Flink笔记

Flink笔记 2.Flink学习笔记2.1流式处理对比2.2 Flink核心概念2.2.1并行度2.2.2算子链2.2.3任务槽 2.3 DataStream2.3.2 读取数据源-源算子&#xff08;Source&#xff09;2.3.3 转换算子&#xff08;Transformation&#xff09; 2.Flink学习笔记 2.1流式处理对比 学习Spark S…

ubuntu22.04上如何创建有privilege权限,有固定自定义IP的空容器

需求背景&#xff1a; 我想用docker来隔离自己的主机环境&#xff0c;来创建一个隔离的空白全新的开发环境&#xff0c;并且使之有固定的IP&#xff0c;在里面可以自由更新下载各种编译依赖&#xff0c;具有privileged权限的容器&#xff0c;以下是操作实现的具体步骤 查看do…

1.12 springboot 整合log4j打印日志

1.除去springboot自带的日志 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><!-- 除去springboot自带的日志 --><exclusion><groupId>org.sprin…

在vscode中运行Hbuilder创建的项目

想必习惯使用vscode的人突然使用HBuilder很不习惯吧&#xff0c;但是HBuilder创建的项目本身没有调试功能。当你有一个app项目但又不想使用HBuilder编写&#xff0c;需要浏览器调试的时候&#xff0c;你这时就需要一个插件了&#xff1a;uni run 插件 基于HBuilderX的采用unia…

JVM运行时区域——对象创建内存分配过程

新创建的对象&#xff0c;都存放在伊甸园区域&#xff0c;当垃圾回收时&#xff0c;将伊甸园区域的垃圾数据销毁&#xff0c;然后将存活的对象转移到幸存者0区域&#xff0c;之后创建的新的对象还是存放在伊甸园区域&#xff0c;等到再次垃圾回收后&#xff0c;将伊甸园区域和幸…

Aurix TC3xx系列MCU ADC采集时间计算方法(四)

文章目录 1 前言2 各阶段时间的计算方法2.1 计算公式2.2 采样阶段2.3 转换阶段2.4 降噪阶段2.5 校准阶段3 采集时间示例>>返回总目录<< 1 前言 在项目开发前期评估阶段,会比较关注ADC的采集时间,我们可以给出一个大概的采样的时间0.5us~1.2us左右,但是对于精确…

SpringCloud nacos 集成 feign 实例

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

创建型设计模式-4.原型设计模式

创建型设计模式-4.原型设计模式 原型设计模式&#xff08;Prototype Design Pattern&#xff09;是一种创建型设计模式&#xff0c;旨在通过复制现有对象来创建新对象&#xff0c;而不是通过使用构造函数进行创建。它允许我们通过克隆&#xff08;复制&#xff09;现有对象的实…

【C++】开源:跨平台轻量日志库easyloggingpp

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍跨平台轻量日志库easyloggingpp。 无专精则不能成&#xff0c;无涉猎则不能通。。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&am…

【SQL应知应会】表分区(四)• MySQL版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 分区表 • MySQL版 前言一、分区表1.非分区表2.分区…