详解Rust结构体struct用法

ops/2024/11/24 16:32:55/

文章目录


Rust的结构体(struct)是一种自定义的数据类型,允许开发者命名和包装多个相关的值,以形成有意义的数据组合。在Rust中结构体不仅用于数据组织,还密切结合了Rust的内存安全性和所有权模型特性,在开发系统编程过程中很有用。

定义和实例化

使用struct关键字来定义一个结构体。可以定义字段的类型,但所有字段都必须在创建实例时进行指定。

rust">struct User {username: String,email: String,sign_in_count: u64,active: bool,
}
//创建结构体  
//初始化实例时,每个字段都需要进行初始化
//初始化时的字段顺序不需要和结构体定义时的顺序一致
let user1 = User {email: String::from("example@example.com"),username: String::from("someusername123"),active: true,sign_in_count: 1,
};//通过.操作符即可访问结构体实例内部的字段值 
user1.email = String::from("anotheremail@example.com");  //通过旧的结构体内容初始化新的结构体
let user2 = User {active: user1.active,username: user1.username,email: String::from("another@example.com"),sign_in_count: user1.sign_in_count,};
//缺省的初始化复制     
//除了email之外 其它变量都用user1的 username字段发生了所有权转移  作为结果 user1无法再被使用  
//把结构体中具有所有权的字段转移出去后,将无法再访问该字段,但是可以正常访问其它的字段   
let user2 = User {email: String::from("another@example.com"),..user1}; 

可变性

在Rust中结构体实例的可变性由整个实例来控制。如果一个实例被声明为可变的,那么所有的字段都是可变的。

rust">//声明可变的结构体  
let mut user1 = User {// 初始化代码相同
};
user1.email = String::from("another@example.com");

构造函数

Rust没有专门的构造函数语法,但可以通过实现关联函数(通常是new)来模拟构造函数行为。

rust">struct User {username: String,email: String,sign_in_count: u64,active: bool,
}
impl User {fn new(email: String, username: String) -> User {User {email,username,active: true,sign_in_count: 1,}}
}let user2 = User::new(String::from("test@test.com"), String::from("testuser"));

方法定义

结构体可以包含方法方法是定义在impl块中的函数,它们可以访问结构体的字段和其他方法。 Rust中的函数分为两类:
1.实例方法(有self参数) 直接作用于结构体的实例,可以访问和修改实例的属性。
2.关联函数(无self参数) 与结构体类型关联,但不作用于具体的实例。它们常用于执行不需要直接访问结构体字段的操作,如构造新实例。

方法包含self参数时,它们是实例方法。这意味着它们操作的是结构体的一个具体实例,可以访问和修改实例的数据。
self 参数可以以三种形式出现:

  • self: 这种方式获取结构体的所有权,通常用于需要消耗结构体实例的场景。
  • &self: 这是最常见的形式,代表对结构体实例的不可变引用,用于当方法只需要读取而不修改结构体数据时。
  • &mut self: 代表对结构体实例的可变引用,用于需要修改实例数据的方法

不包含self参数的方法被称为关联函数。这些函数与结构体类型相关联,但不与结构体的某个具体实例相关联。它们类似于其他语言中的静态方法。关联函数通常用于构造器(创建结构体实例的函数)或与结构体逻辑相关但不依赖于具体实例的工具函数。

rust">impl User {//实例方法  fn email(&self) -> &str {&self.email}//关联函数  fn new(email: String, username: String) -> User {User {email,username,active: true,sign_in_count: 1,}}
}println!("User email: {}", user1.email());

特殊结构体

在Rust中,除了常规的命名字段结构体外,还有两种特殊类型的结构体:元组结构体(tuple structs)和单元结构体(unit structs)。这两种结构体提供了更多的灵活性和表达力,以适应不同的编程需求。

元组结构体本质上是命名元组。它们的字段没有名字,只有类型,适用于需要打包几个数据但不需要为每个数据字段命名的场景。元组结构体的语法和元组类似,但是它有一个具体的类型名称。当你需要创建一个结构体,但字段名不重要或者可以省略时,元组结构体是一个不错的选择。它们简化了代码,尤其是在字段名可能会增加语义重复的情况下。

rust">//元组结构体  
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);  

单元结构体没有任何字段,通常用于表示不需要存储数据的类型,但你想利用它的类型安全优势。它们类似于空元组()(也称为单元类型)。单元结构体的一个用途是实现特定的trait而不存储数据。例如,你可能有一个行为像标记的trait,这些trait通过单元结构体来实现,以便能够用类型系统强制某种特定的行为或属性。常用于标记类型,或在泛型编程中作为类型占位符,还可以在实现无状态的trait时使用。

rust">//单元结构体 没有任何属性  
struct AlwaysEqual;
let subject = AlwaysEqual;
//不关心AlwaysEqual的字段数据,只关心它的行为   
impl SomeTrait for AlwaysEqual {
}

输出结构体

要在Rust中输出结构体的内容,你需要为该结构体实现Debug或Display特性。Debug 特性主要用于调试目的,它会输出结构体的所有字段,而Display特性用于更正式的输出,可以自定义输出内容。

实现Debug特性

rust">#[derive(Debug)]
struct Person {name: String,age: u8
}
let person = Person {name: String::from("Alice"),age: 30
};
// 使用 {:?} 来格式化输出 Debug 版本
println!("{:?}", person); 

实现Display特性

rust">use std::fmt;struct Person {name: String,age: u8
}impl fmt::Display for Person {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {write!(f, "{} is {} years old", self.name, self.age)}
}let person = Person {name: String::from("Alice"),age: 30
};println!("{}", person); // 使用 {} 来格式化输出 Display 版本

与C++结构体的差异

1.内存安全性
Rust结构体在编译时强制执行所有权和借用规则,从而无需担心空悬指针和内存泄漏。C++则依赖于程序员对指针和内存管理的手动控制。

2.方法成员函数
C++允许类成员默认可变。如果你需要不可变性,你必须明确地使用const关键词。而在Rust中,不可变性是默认的,可变性必须明确声明。

3.构造函数和析构函数
C++有构造函数和析构函数的概念,用于初始化和清理资源。Rust则使用所有权系统自动处理资源清理,不需要析构函数,虽然可以实现Drop trait来定制清理行为。

4.继承
C++支持类的继承。而Rust不支持传统的面向对象编程中的继承,而是使用特性(traits)和组合来达到类似的功能复用。


http://www.ppmy.cn/ops/136347.html

相关文章

如何高效集成YS网店客户和组织映射数据到MongoDB

YS网店与客户/组织映射关系对接MongoDB 在企业信息系统的集成过程中,数据的高效流动和准确映射是至关重要的。本文将分享一个具体的技术案例:如何将YS网店中的客户和组织映射关系数据从用友BIP平台集成到MongoDB数据库中。 用友BIP数据获取与处理 首先…

centos7 安装helm v3

文章目录 1. 安装 Helm v3步骤 1:下载 Helm 安装包步骤 2:解压安装包步骤 3:将 Helm 移动到 /usr/local/bin步骤 4:验证安装 2. 使用 Helm 配置 Kubernetes步骤 1:安装并配置 kubectl步骤 2:初始化 Helm步骤…

C语言 蓝桥杯某例题解决方案(查找完数)

蓝桥杯原题: 一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6 1 2 3.编程找出1000以内的所有完数。 这个题没有很大的难点,与我们上一个解决的问题“质因数分解”不同,它不需要判断因数是否是质数,因此…

LeetCode 1861. Rotating the Box

🔗 https://leetcode.com/problems/rotating-the-box 题目 给一个 m * n 的二维数组,# 代表花,* 代表墙二维数组顺时针旋转 90 度,# 会根据重力下落,要么到 * 被挡住,要么到底被挡住返回经过旋转后的二维…

企业办公自动化:Spring Boot OA管理系统详解

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理企业OA管理系统的相关信息成为必然。开发合…

LlamaIndex+本地部署InternLM实践

LlamaIndex本地部署InternLM实践 XTuner是一个调整模型参数的小工具,通过对于给定的大模型输入有限的参数来调整同类型问题的结果输出 ‌LlamaIndex‌是一个将大语言模型(LLMs)和外部数据连接在一起的工具,主要用于增强大模型的知识获取能力…

高级java每日一道面试题-2024年11月22日-JVM篇-说说堆和栈的区别?

如果有遗漏,评论区告诉我进行补充 面试官: 说说堆和栈的区别? 我回答: 在 Java 高级面试中,关于堆和栈的区别是一个常见的问题。堆和栈是 JVM(Java虚拟机)内存模型中的两个重要部分,它们在程序执行过程中扮演着不同的角色。下…

高精度计算题目合集

高精度计算题目合集 1168:大整数加法 1168:大整数加法 1168:大整数加法 高精度加法原理: a,b,c 都可以用数组表示。这些都是基于c语言的算术运算符形成的运算。 c 3 ( c 1 c 2 ) % 10 c_3(c_1c_2)\%1…