news 2026/4/18 7:11:17

Rust 结构体(struct)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rust 结构体(struct)

一、结构体概述

结构体(struct)是 Rust 中的核心复合数据结构,用于将多个不同类型的数据组合成一个逻辑整体,实现对现实事物或抽象概念的建模。它与元组的区别在于:

  • 元组仅通过位置区分元素,无明确名称,访问依赖索引。
  • 结构体为每个字段定义专属名称,无需依赖字段顺序访问,灵活性和可读性更强。

其他编程语言中类似概念包括:JavaScript 的object、C/C++ 的struct、Python 的class(简化数据存储场景)等。

二、结构体基础语法

2.1 定义结构体

结构体定义需包含三个核心部分:关键字struct、结构体名称、若干带类型的字段。

语法格式
struct结构体名称{字段1:数据类型1,字段2:数据类型2,// ... 更多字段}
示例:定义用户结构体
#![allow(unused)]// 允许未使用的变量/结构体,避免编译警告fnmain(){// 定义存储网站用户信息的结构体structUser{active:bool,// 账号是否激活(布尔型)username:String,// 用户名(字符串类型,拥有所有权)email:String,// 邮箱(字符串类型,拥有所有权)sign_in_count:u64// 登录次数(无符号64位整数)}}

2.2 创建结构体实例

创建实例时需为所有字段初始化,字段顺序可与定义时不同。

语法格式
let实例名称=结构体名称{字段1:初始值1,字段2:初始值2,// ... 所有字段需完整初始化};
示例:创建 User 实例
#![allow(unused)]fnmain(){structUser{active:bool,username:String,email:String,sign_in_count:u64}// 创建用户实例,字段顺序与定义时不同(合法)letuser1=User{email:String::from("someone@example.com"),// 邮箱初始化username:String::from("someusername123"),// 用户名初始化active:true,// 激活状态初始化sign_in_count:1// 登录次数初始化};}

2.3 访问与修改结构体字段

通过.操作符访问结构体字段;若需修改字段值,需将实例声明为mut(Rust 不支持单独标记某个字段为可变)。

示例:访问与修改字段
#![allow(unused)]fnmain(){structUser{active:bool,username:String,email:String,sign_in_count:u64}// 声明可变实例letmutuser1=User{email:String::from("someone@example.com"),username:String::from("someusername123"),active:true,sign_in_count:1};// 访问 email 字段并修改值user1.email=String::from("anotheremail@example.com");println!("修改后的邮箱:{}",user1.email);// 输出:修改后的邮箱:anotheremail@example.com}

三、结构体创建简化技巧

3.1 字段同名简化初始化

当函数参数与结构体字段名称相同时,可省略“字段名: 参数名”的重复写法,直接使用字段名初始化。

示例:简化构建函数
#![allow(unused)]fnmain(){structUser{active:bool,username:String,email:String,sign_in_count:u64}// 未简化的构建函数:email: email 和 username: username 重复fnbuild_user_original(email:String,username:String)->User{User{email:email,username:username,active:true,sign_in_count:1}}// 简化后的构建函数:同名字段直接省略赋值符号fnbuild_user_simplified(email:String,username:String)->User{User{email,// 等价于 email: emailusername,// 等价于 username: usernameactive:true,sign_in_count:1}}// 调用简化构建函数letuser2=build_user_simplified(String::from("user2@example.com"),String::from("user2"));}

3.2 结构体更新语法

基于已有结构体实例创建新实例时,可使用..已有实例语法自动复用未显式修改的字段,避免重复赋值。

语法格式
let新实例=结构体名称{需修改的字段:新值,..已有实例// 复用已有实例的其他字段(必须放在最后)};
示例:更新结构体实例
#![allow(unused)]fnmain(){structUser{active:bool,username:String,email:String,sign_in_count:u64}// 已有实例 user1letuser1=User{email:String::from("user1@example.com"),username:String::from("user1"),active:true,sign_in_count:5};// 基于 user1 创建 user3,仅修改 email 字段letuser3=User{email:String::from("user3@example.com"),// 仅修改邮箱..user1// 复用 user1 的 active、username、sign_in_count 字段};}
注意:所有权转移与拷贝

更新语法中,字段的所有权行为取决于其类型是否实现Copy特征:

  • 实现Copy的类型(如boolu64i32):仅拷贝数据,原有实例的字段仍可使用。
  • 未实现Copy的类型(如StringVec<T>):发生所有权转移,原有实例的该字段不可再使用。

示例验证:

#[derive(Debug)]// 用于打印结构体(后续章节讲解)structUser{active:bool,username:String,email:String,sign_in_count:u64}fnmain(){letuser1=User{email:String::from("user1@example.com"),username:String::from("user1"),active:true,sign_in_count:5};letuser3=User{email:String::from("user3@example.com"),..user1};// 合法:active 是 bool 类型(实现 Copy),user1 的 active 未转移println!("user1 的激活状态:{}",user1.active);// 报错:username 是 String 类型(未实现 Copy),所有权已转移到 user3// println!("user1 的用户名:{}", user1.username);// 报错:user1 的 username 已转移,整个 user1 不可再使用// println!("user1: {:?}", user1);}

四、特殊结构体类型

4.1 元组结构体(Tuple Struct)

元组结构体是结构体与元组的结合:有结构体名称,但字段无名称,仅通过位置区分。适用于需要整体标识、但字段含义明确无需命名的场景(如坐标、颜色)。

语法格式
struct结构体名称(字段类型1,字段类型2,...);
示例:定义与使用元组结构体
#![allow(unused)]fnmain(){// 定义元组结构体:Color(存储 RGB 颜色值)、Point(存储 3D 坐标)structColor(i32,i32,i32);structPoint(i32,i32,i32);// 创建实例:无需指定字段名,按位置传值letblack=Color(0,0,0);// 黑色(RGB:0,0,0)letorigin=Point(0,0,0);// 3D 坐标系原点(x=0,y=0,z=0)// 访问字段:通过索引(类似元组)println!("黑色的 R 通道值:{}",black.0);// 输出:0println!("原点的 z 坐标:{}",origin.2);// 输出:0}
注意:不同元组结构体不可混用

即使字段类型完全相同,不同名称的元组结构体仍属于不同类型,不可相互赋值或比较:

#![allow(unused)]fnmain(){structColor(i32,i32,i32);structPoint(i32,i32,i32);letblack=Color(0,0,0);letorigin=Point(0,0,0);// 报错:Color 和 Point 是不同类型// let wrong: Color = origin;}

4.2 单元结构体(Unit-like Struct)

单元结构体无任何字段,类似 Rust 中的单元类型()。适用于仅需类型标识、无需存储数据的场景(如实现特定特征、标记类型)。

语法格式
struct结构体名称;// 无字段,无括号
示例:定义与使用单元结构体
#![allow(unused)]fnmain(){// 定义单元结构体:表示“始终相等”的标记类型structAlwaysEqual;// 创建实例:无需初始化字段letsubject=AlwaysEqual;// 场景:为单元结构体实现特征(后续章节讲解特征时会深入)traitEqual{fnis_equal(&self,other:&Self)->bool;}// 实现 Equal 特征:任何 AlwaysEqual 实例都相等implEqualforAlwaysEqual{fnis_equal(&self,other:&Self)->bool{true// 始终返回 true}}// 使用特征方法leta=AlwaysEqual;letb=AlwaysEqual;println!("a 和 b 是否相等:{}",a.is_equal(&b));// 输出:true}

五、结构体的所有权管理

结构体字段的所有权决定了数据的生命周期和访问权限。默认情况下,结构体应存储拥有所有权的数据(如String),而非引用(如&str),避免生命周期问题。

5.1 存储拥有所有权的数据(推荐)

使用StringVec<T>等拥有所有权的类型作为字段时,结构体完全控制数据的生命周期,无需额外处理借用规则。

示例:正确的所有权设计
#![allow(unused)]fnmain(){structUser{username:String,// 拥有所有权,结构体生命周期独立email:String,sign_in_count:u64}letuser=User{username:String::from("owner_user"),email:String::from("owner@example.com"),sign_in_count:3};// 合法:结构体拥有数据所有权,可正常使用println!("用户名:{}",user.username);}

5.2 存储引用(需生命周期)

若结构体字段为引用(如&str),必须通过生命周期(lifetimes)明确引用的有效范围,否则编译器报错。生命周期的核心作用是确保:结构体的生命周期 ≤ 其引用字段的生命周期。

错误示例:未指定生命周期
// 报错:引用字段缺少生命周期标识structUser{username:&str,// 错误:expected named lifetime parameteremail:&str,// 错误:expected named lifetime parametersign_in_count:u64}fnmain(){letuser=User{username:"error_user",email:"error@example.com",sign_in_count:1};}
正确示例:添加生命周期(后续章节详解)
#![allow(unused)]fnmain(){// 添加生命周期 'a,指定引用字段的生命周期structUser<'a>{username:&'astr,// 引用的生命周期为 'aemail:&'astr,// 引用的生命周期为 'asign_in_count:u64}// 定义字符串字面量(静态生命周期,满足 'a 要求)letusername="valid_user";letemail="valid@example.com";// 创建实例:引用的生命周期 ≥ 结构体生命周期letuser=User{username,email,sign_in_count:2};}

注:生命周期是 Rust 的进阶概念,建议先掌握结构体基础用法,再深入学习。

六、结构体的调试与打印

Rust 默认不支持直接打印结构体(如println!("{}", struct_instance)),需通过Debug特征实现调试打印,或手动实现Display特征实现自定义打印。

6.1 使用#[derive(Debug)]派生 Debug 特征

Debug特征用于调试场景,可通过{:?}{:#?}格式符打印结构体。#[derive(Debug)]是 Rust 提供的自动实现Debug特征的宏。

示例:基础 Debug 打印
// 派生 Debug 特征,允许调试打印#[derive(Debug)]structRectangle{width:u32,height:u32}fnmain(){letrect=Rectangle{width:30,height:50};// 使用 {:?} 打印(紧凑格式)println!("Rectangle 紧凑格式:{:?}",rect);// 输出:Rectangle 紧凑格式:Rectangle { width: 30, height: 50 }// 使用 {:#?} 打印(格式化格式,适合复杂结构体)println!("Rectangle 格式化格式:{:#?}",rect);// 输出:// Rectangle 格式化格式:Rectangle {// width: 30,// height: 50// }}

6.2 使用dbg!宏增强调试

dbg!宏是更强大的调试工具,具有以下特性:

  • 打印文件名、行号、表达式及表达式的值。
  • 输出到标准错误流(stderr),而非标准输出流(stdout),避免与正常输出混淆。
  • 会返回表达式的所有权,不影响后续代码使用。
示例:使用dbg!调试
#[derive(Debug)]structRectangle{width:u32,height:u32}fnmain(){letscale=2;// 使用 dbg! 打印表达式 30 * scale 的值,并返回结果赋值给 widthletrect=Rectangle{width:dbg!(30*scale),// 调试打印:[src/main.rs:10] 30 * scale = 60height:50};// 使用 dbg! 打印 rect 的引用(避免所有权转移)dbg!(&rect);// 调试打印:[src/main.rs:14] &rect = Rectangle { width: 60, height: 50 }}
输出结果
[src/main.rs:10] 30 * scale = 60 [src/main.rs:14] &rect = Rectangle { width: 60, height: 50, }

6.3 手动实现Display特征(自定义打印)

若需更友好的用户级打印格式(如无结构体名称、自定义字段顺序),可手动实现Display特征,使用{}格式符打印。

示例:实现Display特征
usestd::fmt;// 导入 fmt 模块,用于实现 Display// 定义矩形结构体structRectangle{width:u32,height:u32}// 手动实现 Display 特征implfmt::DisplayforRectangle{// f: &mut fmt::Formatter 是格式化输出的写入对象fnfmt(&self,f:&mutfmt::Formatter<'_>)->fmt::Result{// 自定义输出格式:"矩形:宽 30,高 50"write!(f,"矩形:宽 {}, 高 {}",self.width,self.height)}}fnmain(){letrect=Rectangle{width:30,height:50};// 使用 {} 打印(Display 特征)println!("{}",rect);// 输出:矩形:宽 30,高 50}

七、结构体的内存排列

结构体在内存中是连续存储的,但字段的具体排列受内存对齐影响(Rust 会自动优化内存布局,减少内存碎片)。以File结构体为例:

7.1 示例结构体定义

#[derive(Debug)]structFile{name:String,// 字符串类型(底层包含 ptr、len、capacity)data:Vec<u8>// 字节向量(底层包含 ptr、len、capacity)}

7.2 内存布局解析

字段底层组成内存含义
nameptr(指针)指向字符串底层[u8]数组的内存地址
len( usize)字符串的长度(字节数)
capacity(usize)字符串分配的内存容量(字节数,≥ len)
dataptr(指针)指向向量底层[u8]数组的内存地址
len( usize)向量中元素的个数(字节数)
capacity(usize)向量分配的内存容量(元素个数,≥ len)

7.3 核心结论

  • 结构体字段本身存储在连续内存区域,但字段指向的数据(如StringVec<T>的底层数组)存储在堆上,通过指针关联。
  • 结构体拥有其所有字段的所有权,若字段指向堆数据(如String),结构体销毁时会自动释放堆内存(避免内存泄漏)。

八、课后练习与拓展

  1. 基础练习:定义一个Book结构体,包含title(书名,String)、author(作者,String)、pages(页数,u32)、published(是否出版,bool),并创建 2 个实例,使用更新语法基于第一个实例创建第二个实例(修改书名和作者)。

  2. 进阶练习:为Book结构体派生Debug特征,使用dbg!宏调试打印实例;再手动实现Display特征,自定义输出格式(如“《书名》- 作者,共 X 页,出版状态:是/否”)。

  3. 拓展学习:尝试定义一个元组结构体Point2D(存储 2D 坐标,f64, f64),实现一个方法计算两个点之间的距离(使用勾股定理:√[(x2-x1)² + (y2-y1)²])。

九、总结

结构体是 Rust 中实现数据抽象和封装的核心工具,主要特性包括:

  1. 灵活性:支持命名字段,无需依赖字段顺序访问。
  2. 简化语法:字段同名初始化、更新语法减少重复代码。
  3. 特殊类型:元组结构体适合简单标识场景,单元结构体适合类型标记场景。
  4. 所有权安全:默认存储拥有所有权的数据,引用需配合生命周期确保安全。
  5. 调试友好:通过#[derive(Debug)]dbg!宏快速实现调试打印。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:37:28

西门子1200/1500系列PLC的脉冲伺服功能块集成:编程界的新巅峰

西门子1200 1500系列PLC 脉冲伺服功能块集成 该功能块集成了运动控制块 手自动 报警 报警处理 触摸屏关联 将功能块发挥到了极致&#xff01; 完美诠释了功能块的的意义&#xff01;&#xff01; 功能块的颠覆者&#xff01;&#xff01; 看懂这样的功能块编程思路后 你会…

作者头像 李华
网站建设 2026/4/18 3:38:07

这里有个坑:Gazebo图像默认带噪声,得先做高斯模糊

ROS系统下gazebo环境中&#xff0c;无人机结合目标跟踪算法&#xff08;SiamCar&#xff09;&#xff0c;完成对物体的跟踪&#xff08;可以是小车或者其他的&#xff09;&#xff0c;然后给出轨迹对比图等评估指标。 开发语言&#xff1a;python 仿真平台&#xff1a;PIXHAWK …

作者头像 李华
网站建设 2026/4/18 3:37:44

数据合规律师必考七大证书:全面提升职场竞争力

在企业的数字化转型浪潮中&#xff0c;数据合规已成为法律人不可忽视的业务蓝海。随着《数据安全法》《个人信息保护法》等法规的深入实施&#xff0c;数据合规人才需求爆发性增长&#xff0c;具备专业资质的法律人才薪资平均比普通法务高出400%。一、CISP&#xff08;注册信息…

作者头像 李华
网站建设 2026/4/16 21:25:05

Python+Vue的在线考试系统的实现 Pycharm django flask

这里写目录标题 项目介绍项目展示详细视频演示技术栈文章下方名片联系我即可~解决的思路开发技术介绍性能/安全/负载方面python语言Django框架介绍技术路线关键代码详细视频演示 收藏关注不迷路&#xff01;&#xff01;需要的小伙伴可以发链接或者截图给我 项目介绍 随着科技…

作者头像 李华
网站建设 2026/4/15 6:08:45

热门Python库存在元数据投毒攻击漏洞

Hugging Face模型中使用的热门AI和机器学习Python库存在漏洞&#xff0c;这些库的下载量达到数千万次。该漏洞允许远程攻击者在元数据中隐藏恶意代码&#xff0c;当加载包含被投毒元数据的文件时&#xff0c;恶意代码会自动执行。受影响的开源库包括NeMo、Uni2TS和FlexTok&…

作者头像 李华