【Rust指南】解析struct 结构体的定义、实例化、方法、所有权、关联函数

简介: 【Rust指南】解析struct 结构体的定义、实例化、方法、所有权、关联函数

一、结构体的定义


Rust 中的结构体与元组(Tuple)都可以将若干个类型不一定相同的数据捆绑在一起形成整体,但结构体的每个成员和其本身都有一个名字,这样设计有利于访问其成员。元组常用于非定义的多值传递,而结构体用于规范常用的数据结构,且结构体内的成员被称为字段


结构体定义的语法:


struct User{
    username:String,
    sex:String,
    age:u32,
    height:u32
}


乍一看跟C/C++很相似,但是Rust里的结构体每定义一个字段都要加上逗号且最后一个字段不需要加任何符号。

每个字段需要指定具体的数据类型。

同时值得注意的是:结构体不允许在定义的时候实例化。 我们知道C/C++语言是可以在最后的大括号后面实例化的,但是Rust没有这一特点且大括号后面不能加分号。


二、结构体实例化


Rust 很多地方受 JavaScript 影响,在实例化结构体的时候用 JSON 对象的 key: value 语法来实现定义:


为每个字段指定具体值

无需按照声明的顺序指定

例如:


let demo=User{
    username: String::from("微凉秋意"),
    sex: String::from("男"),
    age:22,
    height:183
};


记住一般格式:


结构体类名 {

字段名 : 字段值,

}


如果正在实例化的结构体有字段名称和现存变量名称一样的,可以简化书写:


let username=String::from("微凉秋意");
let sex=String::from("男");
let demo=User{
    username,    // 等同于 username : username,
    sex,
    age:22,
    height:183
};


有这样一种情况:你想要新建一个结构体的实例,其中大部分属性需要被设置成与现存的一个结构体属性一样,仅需更改其中的一两个字段的值,可以使用结构体更新语法:


let demo1=User{
    username:String::from("叶落秋白"),
    sex:demo.sex.clone(),
    ..demo
};


注意:


..demo 后面不可以有逗号。这种语法不允许一成不变的复制另一个结构体实例,意思就是说至少重新设定一个字段的值才能引用其他实例的值。


sex:demo.sex.clone()这里如果不单独写出来编译器会报错:borrow of moved value



意思就是租借了没有所有权的变量,由于无法租借所有权,报错也很正常。而导致错误的原因就是demo.sex的所有权已经在赋值的时候转移给了demo1.sex,这点要尤其注意。因此写成demo.sex.clone()就可以解决所有权转移的问题了。


如果想要修改实例的成员属性,需要在定义的时候把结构体改为可变,也就是加上mut关键字


一旦struct实例是可变的,那么实例中所有的字段都是可变的

无法单独为某一个字段设置为可变


三、元组结构体


元组结构体是一种形式是元组的结构体,与元组的区别是它有名字和固定的类型格式。

它存在的意义是为了处理那些需要定义类型(经常使用)又不想太复杂的简单数据:


//定义:
struct Color(u8,u8,u8);
struct Point(u8,u8,u8);
//实例:
let black=Color(0,0,0);
let origin=Point(0,0,0);


上面定义的black和origin尽管字段类型一致,但是二者是不同的类型,属于两个元组结构体实例。

访问元组结构体与访问元组方法一致,通过点标记法即可

补充一个特殊的单元结构体


例如:


struct UnitStruct;


我们称这种没有身体的结构体为单元结构体(Unit Struct)


可以定义没有任何字段的struct

适用于需要在某个类型上实现某个trait(接口,没有具体实现)


四、结构体所有权


结构体必须掌握字段值所有权,因为结构体失效的时候会释放所有字段。


这也是上面字符串类型使用String而不是用&str(字符串切片)的原因

不过结构体是可以定义引用类型字段的,这需要结合后面的”生命周期“实现


五、结构体方法


Rust中 的方法和函数类似,只不过它是用来操作结构体实例的。在C++语言中,我们知道类内的this指针指向的是对象自身,而Rust 语言不是面向对象的,从它所有权机制的创新可以看出这一点。但是面向对象的珍贵思想可以在 Rust 实现。


结构体方法的第一个参数必须是 &self,不需声明类型,因为 self 不是一种风格而是关键字。


计算一个矩形的面积的方法:


#[derive(Debug)]
struct Rectangle{
    width:u32,
    length:u32
}
impl Rectangle {
    fn area(&self)->u32{
        self.width*self.length
    }
}
fn main(){
    let rect=Rectangle{
        width:15,
        length:20
    };
    println!("rect.area={}",rect.area());
    println!("rect = {:#?}",rect);
}


这里的#[derive(Debug)]是Rust提供的调试库

在程序前面加上之后在println和print宏中就可以用 {:?}或{:#?} 占位符输出一整个结构体

不带#的占位符是输出一行,而带的占位符输出和实例化一样形式的结构体

为结构体添加方法需要在impl里进行,格式就是impl+结构体类型{}

在调用结构体方法的时候不需要填写 self ,这是出于对使用方便性的考虑。

一个多参数的例子:


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));
}
//运行结果为false,证明rect1没有rect2宽度大

六、结构体关联函数


之所以"结构体方法"不叫"结构体函数"是因为"函数"这个名字留给了这种函数:它在 impl 块中没有 &self 参数。


这种函数不依赖实例,但是使用它需要声明是在哪个 impl 块中的。


一直使用的 String::from 函数就是一个"关联函数"。


接下来看一个实例:


#[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);
}


可以看到在Rectangle块中定义了一个create函数,返回值是一个Rectangle类型,而内部实现就是实例化结构体的一个过程。那么这时候在主函数调用该关联函数就可以创建不同的Rectangle结构体实例了。

目录
打赏
0
0
0
0
89
分享
相关文章
深入解析PDCERF:网络安全应急响应的六阶段方法
PDCERF是网络安全应急响应的六阶段方法,涵盖准备、检测、抑制、根除、恢复和跟进。本文详细解析各阶段目标与操作步骤,并附图例,助读者理解与应用,提升组织应对安全事件的能力。
907 89
浅析JVM方法解析、创建和链接
上一篇文章《你知道Java类是如何被加载的吗?》分析了HotSpot是如何加载Java类的,本文再来分析下Hotspot又是如何解析、创建和链接类方法的。
433 132
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
348 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
Bilibili直播信息流:连接方法与数据解析
本文详细介绍了自行实现B站直播WebSocket连接的完整流程。解析了基于WebSocket的应用层协议结构,涵盖认证包构建、心跳机制维护及数据包解析步骤,为开发者定制直播数据监控提供了完整技术方案。
|
4月前
|
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
134 1
深入解析 vsftpd 2.3.4 的笑脸漏洞及其检测方法
本文详细解析了 vsftpd 2.3.4 版本中的“笑脸漏洞”,该漏洞允许攻击者通过特定用户名和密码触发后门,获取远程代码执行权限。文章提供了漏洞概述、影响范围及一个 Python 脚本,用于检测目标服务器是否受此漏洞影响。通过连接至目标服务器并尝试登录特定用户名,脚本能够判断服务器是否存在该漏洞,并给出相应的警告信息。
462 84
|
4月前
|
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
174 5
个人和团队都好用的年度复盘工具:看板与KPT方法解析
本文带你了解高效方法KPT复盘法(Keep、Problem、Try),结合看板工具,帮助你理清头绪,快速完成年度复盘。
504 7
个人和团队都好用的年度复盘工具:看板与KPT方法解析
提升开发效率:看板方法的全面解析
随着软件开发复杂度提升,并行开发模式下面临资源分配不均、信息传递延迟及缺乏全局视图等瓶颈问题。看板工具通过任务状态实时可视化、流量效率监控和任务依赖管理,帮助团队直观展示和解决这些瓶颈。未来,结合AI预测和自动化优化,看板工具将更高效地支持并行开发,成为驱动协作与创新的核心支柱。
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
533 14

热门文章

最新文章

推荐镜像

更多
  • DNS
  • AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等