Rust中的结构体

专栏简介:本专栏作为Rust语言的入门级的文章,目的是为了分享关于Rust语言的编程技巧和知识。对于Rust语言,虽然历史没有C++、和python历史悠远,但是它的优点可以说是非常的多,既继承了C++运行速度,还拥有了Java的内存管理,就我个人来说,还有一个优点就是集成化的编译工具cargo,语句风格和C++极其相似,所以说我本人还是比较喜欢这个语言,特此建立这个专栏,作为学习的记录分享。

日常分享:每天努力一点,不为别的,只是为了日后,能够多一些选择,选择舒心的日子,选择自己喜欢的人!


结构体的定义和实例化

和c++/c语言一样,Rust语言也有结构体,和元组一样,结构体中的变量数据类型是可以不同的,而且只需要引用符就可以得到其中的数据。

struct Buffer{length:usize,width:usize,flag:bool,name:String,
}fn main() 
{let mut buffer = Buffer{length:90,width:100,flag:true,name:String::from("test"),}; println!("{}", buffer.length);
}

在数据结构中,c/c++语言均可以用结构体数据类型定义函数,作为函数的返回值类型,在Rust中,也可以作为函数的返回类型。

fn return_Buffer(length:usize,flag:bool)->Buffer {Buffer{length:length,width:100,flag:flag,name:String::from("test"),}
}

 这里的函数就是返回结构体类型,但是重复写了length,flag等字段,可以采用字段初始化简写语法,也就是不用写具体的变量值,只需要写字段名,但前提是参数和字段名是一样的。

fn return_Buffer(length:usize,flag:bool)->Buffer {Buffer{length,width:100,flag,name:String::from("test"),}
}

结构体更新语法

struct Buffer{length:usize,width:usize,flag:bool,name:String,
}fn main() 
{let mut buffer = Buffer{length:90,width:100,flag:true,name:String::from("test"),}; let mut buffer_two=Buffer{length:buffer.length,width:50,flag:false,name:buffer.name,};let mut buffer_three = Buffer{flag:true,..buffer_two};println!("{}", buffer.length);}
fn return_Buffer(length:usize,flag:bool)->Buffer {Buffer{length,width:100,flag,name:String::from("test"),}
}

上面这段例子就是指的是 利用其他的结构体变量来创建一个新的结构体变量。虽然代码看起来比较简单,但是这其中却有很多的知识。比如,在Buffer结构体中定义了一个String数据类型的变量,那么我们在利用buffer创建buffer_two后,buffer就失去了作用,这一点在前面的数据类型章节中已经讲过了。同样的道理,在创建buffer_three变量的时候,我们先是定义了flag字段的值,然后剩于字段全部使用buffer_two变量,由于也使用了name字段,所以buffer_two变量失去作用,但是如果只是使用其他的字段,则依然有效。如下:

  let mut buffer_three = Buffer{name:String::from("alice"),..buffer_two};println!("{}",buffer_two.name);

这里声明一下,虽然buffer已经丧失作用,这只是代表这个变量不再有作用,但是他当中的非string类型字段还有作用,因为他们本身就已经是变量了。

使用没有命名字段的元组结构体来创建不同的类型

元组结构体,利用元组来对结构体进行定义,或者说是以元组的方式来进行定义结构体。

struct Bufferlines(i32,i32,i32);
struct Bufferline(u32,u32,u32);
let mut buffer_fine=Bufferline(32,34,45);let mut buffer_fine_two = Bufferlines(32,34,45);println!("{}",buffer_fine.1);

没有任何字段的类单元结构体

在Rust语言中,有一种结构体叫做类单元结构体,其实这个大概意思就是说定义一个结构体,但是不包含任何的数据类型,也就相当于一个空壳。

struct Bufferclong; //定义一个类单元结构体
let mut buffer_four=Bufferclong; //定义一个类单元结构体变量。

结构体示例程序

结构体的使用在很多的方面都可以使用,在c语言中的数据结构的学习中就用到了结构体,结构体的实用性在于它可以存储很多数据类型的变量,使得代码简介,增强代码的完整性。

//定义一个结构体
struct Rectangle
{width: usize,height: usize,
}fn main(){let mut rect = Rectangle{width: 30,height: 50,};println!("The area is {}",area(&rect));}
fn area(rect: &Rectangle) ->usize { return rect.width * rect.height;  //如果不使用return关键字,则不需要分号
}

这里我们定义了一个结构体,定义了width和height两个字段 ,类型定义的时usize(无论是32位的还是64位的电脑都可以跑),然后就是创建了一个实体,并对字段赋值。然后调用已经定义好的求面积函数。在这里要特别说明一下,定义函数的时候,参数是结构体的引用类型了,这个原因和前面讲解的所有权属性有关。如果我们不使用引用,则在传参后原本定义的结构体实例对象就无法再使用了。所以这里使用引用也是为了后面可以继续使用该对象。

通过派生trait增加实用功能

我们在使用println!()宏的时候,虽然可以输出绝大多数的数据内容,但是却无法直接输出结构体实体对象。那么有什么方法嘛?

我们先来看一下使用println!()会报什么错误。

`Rectangle` doesn't implement `std::fmt::Display`
  --> src/main.rs:14:17
   |
14 |   println!("{}",rect);
   |                 ^^^^ `Rectangle` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `Rectangle`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = 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)

上面的这段报错,我们可以看到报错给出了解决方法,我们尝试使用报错给出的解决方法试试:

println!("{:#?}",rect);
   |                    ^^^^ `Rectangle` cannot be formatted using `{:?}`

我们发现还是报错,不过报错给出了一个信息,增加一个Debug。

//定义一个结构体
#[derive(Debug)]
struct Rectangle
{width: usize,height: usize,
}fn main(){let mut rect = Rectangle{width: 30,height: 50,};println!("The area is {}",area(&rect));println!("{:#?}",rect);}
fn area(rect: &Rectangle) ->usize { return rect.width * rect.height;  //如果不使用return关键字,则不需要分号
}

通过 上面增加的显示代码,我们已经可以打印出结构体了。那么还有没有其他的简单的方法?

dbg!宏

dbg!宏和println!宏有点不一样的是,dbg!宏接受的是表达式的所有权,而println!接受的是引用。这个如果不理解的话,下面看一个例子。

//定义一个结构体
#[derive(Debug)]
struct Rectangle
{width: usize,height: usize,
}fn main(){let mut rect = Rectangle{width: 30,height: 50,};println!("The area is {}",area(&rect));println!("{:#?}",rect); //第一次打印println!("{:?}",rect);//第二次使用println!打印//使用dbg!打印dbg!(rect); //第一次打印dbg!(rect);//第二次打印,此时运行错误,rect已经便不再具有所有权。}
fn area(rect: &Rectangle) ->usize { return rect.width * rect.height;  //如果不使用return关键字,则不需要分号
}

上面使用println!宏可以连续打印,但是使用dbg!宏只能打印一次,此后这个变量就不再具有所有权。所有权已经转移。所以运行上述代码就会出现报错。

let mut rect = Rectangle{
   |       -------- move occurs because `rect` has type `Rectangle`, which does not implement the `Copy` trait
...
18 |   dbg!(rect); //第一次打印
   |   ---------- value moved here
19 |   dbg!(rect);//第二次打印,此时运行错误,rect已经便不再具有所有权。
   |        ^^^^ value used here after move

 如果不想让dbg!获取表达式的所有权,那么就在调用dbg!的时候传递引用参数。诸如dbg!(&rect);

impl语法

前面我们定义的area函数,如果需要使用则需要进行传参,重要的是,我们定义的很多函数可能不会调用结构体变量,但是我们在与结构体有关的函数时,就会显得非常麻烦。为了解决这种麻烦,Rust推出了impl语法。

定义方法

我们先来看一个实例:

//定义一个结构体
#[derive(Debug)]
struct Rectangle
{width: usize,height: usize,
}
impl Rectangle {fn area(&self) ->usize { return self.width * self.height;  //如果不使用return关键字,则不需要分号}
}fn main(){let mut rect = Rectangle{width: 30,height: 50,};println!("{}", rect.area());}

 上面的代码中,我们用impl定义了一个快,这个块中的所有内容都将与Rectangle类型相关联。参数使用的是&self,这是用来替代rectangle:&Rectangle。这就表示我们可以使用结构体实例中的属性。每个与结构体关联的函数,第一个参数都必须是&self,后面的参数根据自己需要来添加。

Rust中的自动引用和解引用:

C/C++中有解引用和引用,这两个概念相信很多熟悉c++的小伙伴都知道这个概念,这里就不过多介绍了。

Rust中取消了这种复杂的机制,也就是说我们不需要考虑是否需要解引用,直接用属性操作符就可以了。

 多个参数

//定义一个结构体
#[derive(Debug)]
struct Rectangle
{width: usize,height: usize,
}
impl Rectangle {fn area(&self) ->usize { return self.width * self.height;  //如果不使用return关键字,则不需要分号}fn hold(&self,other: &Rectangle) -> bool {self.width > other.width && self.height > other.height}
}fn main(){let mut rect = Rectangle{width: 30,height: 50,};let mut rect2 = Rectangle{width:40,height:50,};let mut rect3 = Rectangle{width:20,height:30,};println!("{}", rect.hold(&rect2));println!("{}", rect2.hold(&rect3));}

上面这段代码可能很多人没看懂是什么意思,其实简单的看就是,在impl块中又定义了一个比较长宽大小的函数,而比较明显的是,多了一个参数(other:&Rectangle),这是表示我们这里使用另一个Rectangle实例,不再是使用一个结构体实例。那么如果我们要多几个不是结构体实例的参数又该如何?

 fn get_v(&self,length:usize) -> usize {self.width * self.height*length}

在impl块中增加上述代码,便可以得到长方体的体积,这里新增加的一个参数,使用的是usize类型。为什么不使用other喃?

impl块中函数的多个参数的设定规则:

1、如果要使用多个参数,那么第一个参数必须是&self。

2、如果要使用统一个结构体类型的不同实例,则需要添加other来区分。

3、如果只是增加普通的参数,则直接在后面添加即可。

关联函数

在官方的定义中,所有在impl块中的函数均被称作关联函数,我们前面说,如果要定义一个结构体的关联函数,那么第一个参数必须是&self,但是我们也可以不使用&self作为第一个参数,但是这就不再算是方法,并不能作用于结构体的实例。

不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。这些函数的名称通常为 new ,但 new 并不是一个关键字。

例如:

impl Rectangle{fn new(size:usize,length:usize)->Self{Self{width:size,height:length,}}
}

然后在主函数中调用:

let mut rects = Rectangle::new(34,45);

println!("{}", rects.area());

这里的调用方式不再是使用实例进行调用,而是使用::进行调用,这个是由于该函数只是被建立在结构体的命名空间中,而未获得实例,所以需要使用::进行调用,后面会细讲。

多个impl块

在Rust语言中,一个结构体是允许存在多个impl块的,就像上述的代码一样,关于这个性能,后面需要的时候再慢慢讲解,初学者只需要指导就好,下面是本节内容的所有代码,如果相对本节知识有所了解的话,可以仔细看一下。

//定义一个结构体
#[derive(Debug)]
struct Rectangle
{width: usize,height: usize,
}
impl Rectangle {fn area(&self) ->usize { return self.width * self.height;  //如果不使用return关键字,则不需要分号}fn get_v(&self,length:usize) -> usize {self.width * self.height*length}fn hold(&self,other: &Rectangle) -> bool {self.width > other.width && self.height > other.height}
}impl Rectangle{fn new(size:usize,length:usize)->Self{  //相当于是结构体的构造函数Self{width:size,height:length,}}
}
fn main(){let mut rects = Rectangle::new(34,45);println!("{}", rects.area());let mut rect = Rectangle{width: 30,height: 50,};let mut rect2 = Rectangle{width:40,height:50,};let mut rect3 = Rectangle{width:20,height:30,};println!("{}", rect.hold(&rect2));println!("{}", rect2.hold(&rect3));println!("The rect2 体积是:{}",rect2.get_v(20));}

总结

本节的内容主要就是讲解结构体中的相关知识,通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。在 impl 块中,你可以定义与你的类型相关联的函数,而方法是一种相关联的函数,让你指定结构体的实例所具有的行为。

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

钾和钠含量

声明 本文是学习GB-T 397-2022 商品煤质量 炼焦用煤. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了炼焦用商品煤产品质量等级和技术要求、试验方法、检验规则、标识、运输及贮存。 本文件适用于生产、加工、储运、销售、使用…

MySQL索引事务

一 、 索引 索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。 可以对表中的一列或多列创建索引, 并指定索引的类型,各类索引有各自的数据结构实现。 索引保存的数据结构主要为B树,及hash的方式。 1. 作用 数据库中…

网络安全攻防对抗之隐藏通信隧道技术整理

完成内网信息收集工作后,渗透测试人员需要判断流量是否出得去、进得来。隐藏通信隧道技术常用于在访问受限的网络环境中追踪数据流向和在非受信任的网络中实现安全的数据传输。 一、隐藏通信隧道基础知识 (一)隐藏通信隧道概述 一般的网络通…

【C++】String类基本接口介绍(多看英文文档)

string目录 目录 如果你很赶时间,那么就直接看我本标题下的内容即可!! 一、STL简介 1.1什么是STL 1.2STL版本 1.3STL六大组件 1.4STL重要性 1.5如何学习STL 二、什么是string??(本质上是一个类&…

Spring Boot虚拟线程的性能还不如Webflux?

早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。 测试场景 作者采用了一个尽可能贴近现实操作的场景: 从授权头信息中提取JWT验证…

排序算法:非比较排序(计数排序)

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关排序算法的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通…

三子棋小游戏(简单详细)

设计总体思路 实现游戏可以一直玩,先打印棋盘,玩家和电脑下棋,最后分出胜负。 如果编写较大的程序,我们可以分不同模块 例如这个三子棋,我们可以创建三个文件 分别为: game.h 函数的声明game.c 函数…

Linux 快捷键

1、快捷键小操作 1.1、ctrl c 强制停止 Linux某些程序的运行,如果想要强制停止它,可以使用快捷键ctrl c 命令输入错误,也可以通过快捷键ctrl c,退出当前输入,重新输入 1.2、ctrl d 退出或登出 可以通过快捷键&…

python的requests响应请求,结果乱码,即使设置了response.encoding也没有用的解决方法

一、问题 如图: 一般出现乱码,我们会有三种解决方式,如下但是图中解决了发现还是不行, response.encodingresponse.apparent_encoding通过看网页源码对response.encodingutf8指定编码格式或者直接通过response.content.decode()来获得源码 出…

辨析常见的医学数据分析(相关性分析回归分析)

目录 1 常见的三种分类结果? 2 什么是相关性分析? 相关性分析的结果怎么看? 3 什么是回归分析? 1)前提 2)常见的回归模型 4 对于存在对照组实验的医学病例如何分析? 1)卡方检验…

【Newman+Jenkins】实施接口自动化测试

一、是什么Newman Newman就是纽曼手机这个经典牌子,哈哈,开玩笑啦。。。别当真,简单地说Newman就是命令行版的Postman,查看官网地址。 Newman可以使用Postman导出的collection文件直接在命令行运行,把Postman界面化运…

pytest框架前后置设置,以及pytest默认规则

一、pytest框架的默认规则 1、模块名默认必须以test开头或者以test结尾 2、测试类必须以Test开头,并且不能有__init__方法 3、测试方法默认必须以test开头 当然以后的一些默认规则除测试类不能使用__init__方法外其余的都是可配置的,当然一般情况下我们…

C/C++好题分享--代码题

2-1排序子序列 int main() {int n;cin >> n;// 注意这里多给了一个值&#xff0c;是处理越界的情况的比较&#xff0c;具体参考上面的解题思路vector<int> a;a.resize(n 1);//这里有个坑&#xff0c;这个题越界了牛客测不出来&#xff0c;给n,并且不写a[n] 0;不会…

SpringCloud Gateway--Predicate/断言(详细介绍)上

&#x1f600;前言 本篇博文是关于SpringCloud Gateway–Predicate/断言&#xff08;详细介绍&#xff09;上&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以…

Vue中的路由介绍以及Node.js的使用

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Vue》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这个专栏…

数据结构之顺序表

前言 顺序表采用模块化编程思路&#xff0c;顺序表的实现使用3个模块&#xff0c;test.c—测试模块 Seqlist.c—接口函数的实现模块 seqlist.h—接口函数声明 顺序表的基本概念 顺序表是在计算机内存中通常以数组形式存储的线性表&#xff0c;线性表是n个具有相同特性的数据元…

R语言绘制PCA双标图、碎石图、变量载荷图和变量贡献图

1、原论文数据双标图 代码&#xff1a; setwd("D:/Desktop/0000/R") #更改路径#导入数据 df <- read.table("Input data.csv", header T, sep ",")# ----------------------------------- #所需的包: packages <- c("ggplot2&quo…

电视访问群晖共享文件失败的设置方式,降低协议版本

控制面板-文件服务-SMB-高级设置&#xff0c;常规及其他里面配置即可。

【红外图像增强】基于引力和侧向抑制网络的红外图像增强模型(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

计算机视觉: 三维物体生成

三维物体生成与编辑 论文地址: Controllable Mesh Generation Through Sparse Latent Point Diffusion Models 背景 数据是目前数字化和AI领域最宝贵的财富之一&#xff0c;但是对于目前的开发者来说&#xff0c;收集数据都意味着极大的成本。所以建立一个高效的生成模型能极…