news 2026/4/17 3:18:23

第七章 结构体

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第七章 结构体

结构体类似于其他面向对象语言的类,它包含了一些表达某类特性的属性组合,内容是一组属性名和属性值的集合。结构体还包含了对应相关联的函数方法和行为。它和元祖的区别是:

  1. 元祖不包含属性名称,结构体包含属性名称。
  2. 元祖使用小括号,结构体使用大括号。
    1. 结构体的定义和实例化

使用关键字struct加上名称来制定类型为结构体,名称一般使用大写字母开头的英文,名称最好是有含义的英文单词或组合,其后使用大括号包含属性名称,属性名称后面跟着冒号,冒号后面指定属性名称的类型,每个属性组合之间使用逗号进行分割。具体的示例如下所示:

struct User { name: String, email: String, active: bool, sign_in_count: u64, }

为了使用上面定义的结构体,还必须将其实例化,即给每一个定义的属性进行赋值。赋值的语法类似定义,只不过将属性类型替换为值,形成key: value的键值对。属性的赋值顺序不一定要严格按照结构体内定义的顺序。

fn main() { let user = User { active: true, name: String::from("caocao"), email: String::from("caocao@example.com"), sign_in_count: 1, }; println!("{user:?}"); }

如果需要获取结构体中的某个属性的值,使用小数点符号(“.”)来进行声明,例如需要访问用户的电子邮件可以使用user.email的方式。如果需要修改属性的值,实现化结构体的时候需要将其声明为可变变量,使用符号“mut”作为修饰符。如下面的示例所示:

fn main() { let mut user = User { active: true, name: String::from("caocao"), email: String::from("caocao@example.com"), sign_in_count: 1, }; println!("{0}",user.email); user.email = String::from("liubei@example.com"); println!("{0}",user.email) }

注意:整个结构体实例必须都是声明为可变的,Rust不允许只标注某一个属性是可变的。

和其他表达式一样,我们也可以将实例一个结构体作为一个表达式在某个函数的结尾作为返回值。如下所示:

fn build_user(email: String, name: String)->User { User { active: true, email: email, name: name, sign_in_count: 1, } }

从上面的示例可以看到email: email,name: name,似乎显得有点冗余,Rust提供了一种简写形式,如果参数名和属性名一致,可以只写一个。如下所示:

fn build_user(email: String, name: String)->User { User { active: true, email, name, sign_in_count: 1, } }

是不是简约了许多。

      1. 两个结构体实例移植的更新语法

我们经常会遇到从一个结构体复制一些值到一个新的结构体。这时就可以使用结构体的更新语法。下面的示例是创建user2,从user实例内复制一个值,没有使用更新语法的示例:

let user2 = User { active: user.active, name: user.name, email: String::from("zhouyu@example.com"), sign_in_count: user.sign_in_count, };

我们可以使用符号“..”来获取源机构体的其余属性值。下面是使用更新语法的示例:

let user2 = User { email: String::from("zhouyu@example.com"), ..user };

..user表明该结构体其他属性值来自user。它必须在结构体的最后。

      1. 使用元祖结构体

Rust也提供了一种“元祖结构体”,类似与元祖类型,但是它使用了struct关键字进行了声明。元祖机构图没有使用属性名,而是使用类型来定义成员,但是它指定了整个结构的名称,用于表明此元祖非彼元祖。可能是如果给每个属性指定名称显得冗长而且多余,适用于人们清楚的知道每个成员属性的名称,例如:颜色值,或者坐标值。

#[derive(Debug)] struct Color(i32,i32,i32); #[derive(Debug)] struct Point(i32,i32,i32); fn main() { let black = Color(0,0,0); let origin = Point(0,0,0); println!("{black:?}"); println!("{origin:?}"); }

注意:black和origin是不同的类型,因为它们使用了不同的元祖结构进行了实例化。每种结构都定义了它们自己的类型,虽然它们的值是一样的,且它们的结构体内容也是一致的。你也可以使用元祖的解构方式对元祖机构进行解构;例如:let Point(x, y, z) = origin;。如果要访问器成员也可以使用“.”加上其索引值;例如black.0。

      1. 单元类型的结构体

你也可以定义不包含任何属性的结构体,我们称之为单元类结构体。这就像类型“()”,单元类结构体适用于只需要函数和方法的定义,这就像其他语言中的接口。

struct AlwaysEqual; fn main() { let subject = AlwaysEqual; }

我们使用struct定义了结构体AlwaysEqual,后面直接使用分号结尾,没有花括号,也没有属性。如果要实例化,直接将其复制给变量就可以了,也不用对其的属性进行赋值。

      1. 结构体中数据的所有权

在之前的User结构体中,我们使用了String类型,而不是&str的字符串切片类型,这是故意而为之,因为我们需要实例化这个User之后,拥有它所有数据的所有权,这样整个结构体在整个生命周期中的数据都是有效的。

当然,有时也是需要保存数据的引用值,这时就需要使用生命周期的概念了。它可以确保其和结构体一样的生命周期。看看下面使用引用而没有规定其声明周期的示例,它在编译期间就会报错:

struct User { active: bool, name: &str, email: &str, sign_in_count: u64, } fn main() { let user1 = User { active: true, name: "caocao", email: "caocao@example.com", sign_in_count: 1, }; }

系统报错信息:

在报错的提示信息中已经提示了如何修改。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 3:17:12

【FreeRTOS】深入解析消息队列的阻塞机制与任务通信实战

1. 为什么需要消息队列? 在嵌入式开发中,任务间的数据传递是个永恒的话题。记得我刚接触FreeRTOS时,第一反应就是用全局变量来传递数据——这不就跟裸机编程一样简单直接吗?但很快就被现实狠狠教育了。有一次在电机控制项目中&…

作者头像 李华
网站建设 2026/4/17 3:13:12

遥感数字图像处理教程【2.3】

5 . 3 系 统 辐 射 校 正 1 . 边缘减光现象 在使用透镜的光学系统中,由于透镜光学的非均匀性,在成像平面上边缘部分比中间部分暗,即边缘减光。对于这种问题,如果光线以平行于主光轴的方向通过透镜到达像平面的光 强 度 为 其&am…

作者头像 李华
网站建设 2026/4/17 3:11:57

嵌入式网络通信中数据链路层的核心技术与优化实践

1. 嵌入式网络通信中的数据链路层核心价值 在当今这个万物互联的时代,嵌入式系统设计师面临着一个关键转折点——网络连接能力已成为嵌入式设备的标配而非选配。作为OSI七层模型中的第二层,数据链路层扮演着物理比特流与逻辑数据包之间的"翻译官&qu…

作者头像 李华
网站建设 2026/4/17 3:10:25

终极隐私保护:5分钟打造你的Windows本地实时语音转文字系统

终极隐私保护:5分钟打造你的Windows本地实时语音转文字系统 【免费下载链接】TMSpeech 腾讯会议摸鱼工具 项目地址: https://gitcode.com/gh_mirrors/tm/TMSpeech 想要一个完全免费、离线运行、且能实时将任何电脑声音转为文字的助手吗?TMSpeech正…

作者头像 李华