Rust中的结构体

news2024/11/27 14:46:30

专栏简介:本专栏作为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.coloradmin.cn/o/1028794.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能的未来:从 Jetson 到 GPT,沙龙见闻与洞察

前言 在当今数字化时代,人工智能正以惊人的速度改变着我们的生活和工作方式。从智能语音助手到自动驾驶汽车,从智能家居到医疗诊断,人工智能技术已经广泛渗透到各个行业,并为其带来了巨大的变革和创新。越来越多的行业专家、学者…

MQTT服务器搭建

本次搭建的MQTT服务器是emqx提供的服务器 1、下载 https://www.emqx.com/en/downloads/broker 从官网下载5.2.0版本emqx-5.2.0-windows-amd64.zip 下载完成直接安装 2、配置,修改端口号 mqtt默认端口号 常规的用法,我们一般使用和开放这两个端口&am…

037:vue项目监听页面变化,动态设置iframe元素高度

第037个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…

Element-ui 标签页el-radio-button左边框不显示问题

想实现一个tab切换的功能 看tabs组件挺相似的直接拿来用 默认是下边的效果 分开点 发现右边的白的按钮左边框没了 选中右边 左边按钮的边框正常 emmmm 选择按钮后查看样式 发现他有个border-left:0 去掉之后显示正常 在代码里添加border属性后显示正常了 但又出现新的问题…

二、飞线或者路线的实现(TubeBufferGeometry:管道缓冲几何体)

在做three大屏的时候我们经常会遇到绘制发光路线和指向的情况,那么就需要使用到管道(TubeBufferGeometry)这个API。先看看他能达到的几种效果。 一、效果图 1平面效果 2飞线效果 二、那么我们直接看代码,一共有6步。 定义数据 l…

作业错题一

1、内联函数的错题 首先,对内联函数的定义不清楚,同时对内联函数的原理有点模糊,还有一些注意的点忽略了; this指针的理解也出现错误! 下一题是构造函数和析构函数的错误: 构造函数时先定义先构造&#…

无法删除目录(设备或资源忙)

文章目录 无法删除目录(设备或资源忙)问题原因解决方案步骤一:首先找到挂载的位置步骤二:取消挂载步骤三:查看挂在情况 无法删除目录(设备或资源忙) 问题 原因 网络共享挂载导致无法删除 解决…

Java Gradle

目录 1. Gradle简介 1.1 什么是Gradle 1.2 Gradle优点 1.2.1 自动化构建 1.2.2 Gradle相当于构建工具的进化版 1.2.3 对于Gradle优点,官网是这么说的 1.3 Grade 的版本说明 2. Groovy语言基础 2.1 基础语法 2.2 String 与 GString 2.2.1 String 2.2.2 插…

最新SSL证书申请源码,支持API接口,支付在线充值

最新SSL证书申请源码,支持API接口,支付在线充值 目前测试还不需要授权,以后更新版就不知道了 SSL证书保证网络安全的基本保障。向您介绍我们的在线生成SSL证书系统 支持在线生成SSL证书系统,用户登录可在线申请SSL,…

Nacos内核设计之一致性协议(上)

Nacos一致性协议 Nacos技术架构 先简单介绍下Nacos的技术架构 从而对nacos有一个整体的认识 如图Nacos架构分为四层 用户层、应用层、核心层、各种插件 再深入分析下nacos一致性协议的发展过程及原理实现 为什么nacos需要一致性协议 Nacos是一个需要存储数据的一个组件 为了实…

4k、VR与万兆光网

“全光万兆”对VR意义重大。 pico4的分辨率 PICO 4 的单眼分辨率是 2160 2160,整体分辨率高达 4320 2160。这是一款高性能的 VR 一体机,采用了 2.56 英寸的 Fast-LCD 屏幕,最高可实现 90Hz 刷新率,还有 1200 PPI 和 20.6 PPD 的…

基于Python+Tkinter实现一个贪食蛇小游戏

你是否还记得那个时代,当我们的手机还没有触摸屏,游戏也只有像“贪食蛇”这样的经典款?当时,许多人都沉迷于控制一条小蛇吃食物的乐趣中。而今,让我们利用Python和Tkinter,一起重温那个时代,制作…

多平台兼容性:跑腿系统开发的移动端和Web端技术选项

随着移动设备和Web浏览器的广泛使用,跑腿系统的开发需要考虑多平台兼容性。本文将讨论一些在开发跑腿系统的移动端和Web端时可用的技术选项,并提供示例代码以帮助您入门。 移动端技术选项: 1. React Native React Native是一个流行的移动应…

Linux shell编程学习笔记1:关于shell的前世今生

一、什么是Shell? Shell英文单词的原意是“外壳”,在计算机领域专指在操作系统(Operating System)外层,提供用户界面(User Interface)的程序,主要负责将用户的命令(Comma…

优彩云采集器下载-免费优彩云采集器下载地址

免费优彩云采集器。您是否曾为了数据采集而感到头疼不已?是否一直在寻找一种能够轻松、高效地获取所需数据的方法?别着急,让我们一起来了解如何通过优彩云采集器解决这些问题,从而让您产生购买的欲望。 免费全自动采集发布批量管理…

【Docker】Docker的使用案例以及未来发展、Docker Hub 服务、环境安全

前言 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。 作者简介: 辭七七&#xf…

【Elsevier旗下】1区(TOP)、CCF推荐,仅2个月左右录用!

计算机领域 • 新刊速递 出版社:Elsevier 影响因子(2022):8.0-9.0 期刊分区:JCR1区,中科院2区(TOP) 检索数据库:SCIE&EI 双检 数据库检索年份:1992年…

客户端和服务端信息交互模型

什么是客户端和服务端? 客户端:可以向服务器发请求,并接收返回的内容进行处理 服务器端:能够接收客户端请求,并且把相关资源信息返回给客户端的 当用户在地址栏中输入网址,到最后看到页面,中间都…

魔众题库系统 v8.8.0 公式编辑升级,注册站内信和邮件,手机Banner支持视频背景

魔众题库系统基于PHP开发,可以用于题库管理和试卷生成软件,拥有极简界面和强大的功能,用户遍及全国各行各业。 魔众题库系统发布v8.8.0版本,新功能和Bug修复累计23项,公式编辑升级,注册站内信和邮件&#…

【文末送书】计算机网络编程 | epoll详解

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关…