Rust 运算符快速了解

【图书介绍】《Rust编程与项目实战》-CSDN博客

《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com)

Rust编程与项目实战_夏天又到了的博客-CSDN博客

4.1  运  算  符

前面已经学习了变量和常量,本节开始对它们进行操作,这就要用到Rust的操作符(Operator)。操作符通常是由一个或多个特殊的符号组成的(也有非特殊符号的操作符,如as),比如+、−、*、/、%、&、*等。每个操作符都代表一种动作(或操作),这种动作作用于操作数之上。简单来说,就是对操作数执行某种操作,然后返回操作后得到的结果。比如,加法操作3 + 2,这里的+是操作符,加号两边的3和2是操作数,加法符号的作用是对操作数3加上操作数2,得到计算结果5并返回5。

有些语言,很多操作符都是关键字,比如add、equals等。Rust的操作符主要是由符号组成的,比如+、−等。这些符号不在字母表中,但是在所有键盘上都可以找到。这个特点使得Rust程序更简洁,也更国际化。运算符也称操作符。运算符是Rust语言的基础,所以非常重要。

4.1.1  赋值运算符

赋值运算符的功能是将一个值赋给一个变量。比如:

a = 5;

以上代码将整数5赋给变量a。= 运算符左边的部分叫作左值(lvalue,left value),右边的部分叫作右值(rvalue,right value)。左值必须是一个变量,而右值可以是一个常量、一个变量、一个运算的结果,或者是前面几项的任意组合。

有必要强调赋值运算符永远是将右边的值赋给左边,不会反过来。比如:

a = b;

以上代码将变量b的值赋给变量a,不论赋值前a存储的是什么值,这行代码执行后,a的值就和b的值一样了。但要注意,我们只是将b的值赋给a,以后如果b的值改变了,并不会影响a的值。下面来看实例。

【例4.1】  赋值运算符的使用

   在命令行下用命令cargo new myrust新建一个Rust项目,项目名是myrust。

   打开VS Code,再打开文件夹myrust,然后在VS Code中打开src下的main.rs,输入如下    代码:

fn main() {let mut a:i32;let mut b:i32;            //此时a、b的值未知a = 10;                     // a:10,b未知b = 4;                      // a:10,b:4a = b;                      // a:4,b:4b = 7;                      // a:4,b:7println!("{},{}",a,b);
}

以上代码的结果是,a的值为4,b的值为7。最后一行中b的值被改变并不会影响a,虽然在此之前我们声明了a = b;(从右到左规则,right-to-left rule)。

   运行结果如下:

4,7

Rust语言支持5种数学运算符,分别为加(+)、减(−)、乘(*)、除(/)、取模(%),括号里的符号就是数学运算符号。加减乘除运算想必大家都很了解,它们和一般的数学运算符没有区别。

唯一你可能不太熟悉的是用百分号(%)表示的取模运算(Module)。取模运算是取两个整数相除的余数。例如,如果我们写a = 11 % 3;,变量a的值将会为2,因为2是11除以3的余数。比如:

fn main() {let mut a:i32;let mut b:i32;let mut c:i32; a = 11 % 3;                // 取模运算得a为2b = 4+a;                    //加法运算得b为6c =(a+b)/2;                //除法运算得c为4       println!("{},{},{}",a,b,c);
}

输出结果:

2,6,4

4.1.3  组合运算符

Rust以书写简练著称,其一大特色就是这些组合运算符(+=、−=、*=、/=及其他),这些运算符使得只用一个基本运算符就可以改写变量的值:

value += increase; 等同于 value = value + increase;

比如:

  • a −= 5; 等同于 a = a − 5;。
  • a /= b; 等同于 a = a / b;。
  • price *= units + 1; 等同于price = price * (units + 1);。

其他运算符以此类推。下面来看一个组合运算符的例子,代码如下:

fn main() {let mut a:i32;let mut b:i32;let mut c:i32; a = 11 % 3;         // a:2b = 4+a;            // b:6c =(a+b)/2;  //c:3  a+=c;  b*=a;c/=2;   println!("{},{},{}",a,b,c);
}

结果输出:

6,6,4

值得庆幸的是,Rust 语言不支持自增运算符(++)和自减运算符(--),因此本节绝对不会出现类似于a+++++i这样让人血压升高的语句。其实,编程语言由于是给人用的,一定要考虑到人的局限性(就是面对复杂事物容易出错),所以编程语言一定要简单明了,Rust去掉了++和--,相对于C语言而言,绝对是个进步,可以从源头上尽可能防止人类出错。

4.1.4  关系运算符

我们用关系运算符来比较两个表达式,关系运算的结果是一个布尔值,即它的值只能是true或false。例如,我们想通过比较两个表达式来看它们是否相等,或一个值是否比另一个值大。表4-1所示为Rust的关系运算符。

表4-1  Rust的关系运算符

关系运算符

   

==

如果左右值相等,则运算符结果是true,否则是false

!=

如果左右值不相等,则运算符结果是true,否则是false

>

如果左值大于右值,则运算符结果是true,否则是false

<

如果左值小于右值,则运算符结果是true,否则是false

>=

如果左值大于或等于右值,则运算符结果是true,否则是false

<=

如果左值小于或等于右值,则运算符结果是true,否则是false

示例代码如下:

fn main() {let mut a:bool;let mut b:bool;let mut c:bool;a=(7!=5);b = (100<=99);c=(6==6);println!("{},{},{}",a,b,c);
}

运行结果:

rue,false,true

除使用数字常量外,我们也可以使用任何有效表达式,包括变量。比如下列代码:

fn main() {let mut a:i32;let mut b:i32;let mut c:i32;a=2;b=3;c=6;println!("{},{},{}",(a == 5),(a*b >= c),(b+4 > a*c));
}

输出结果:false,true,false。(a*b >= c)返回true是因为它实际是(2*3 >= 6),(b+4 > a*c)返回false因为它实际是(3+4 > 2*6)。

值得注意的是,运算符=(单个等号)不同于运算符==(两个等号),前者是赋值运算符(将等号右边的表达式值赋给左边的变量);后者(==)是一个判断等于的关系运算符,用来判断运算符两边的表达式是否相等。

4.1.5  逻辑运算符

运算符!等同于boolean运算NOT(取非),它只有一个操作数(Operand),写在它的右边。它做的唯一工作就是取该操作数的反面值,也就是说如果操作数值为真(true),那么运算后值变为假(false),如果操作数值为假(false),则运算结果为真(true)。它就好像是取与操作数相反的值。例如:

  • !(5 == 5)返回false,因为它右边的表达式(5 == 5)为真(true)。
  • !(6 <= 4)返回true,因为(6 <= 4)为假(false)。
  • !true返回假(false)。
  • !false返回真(true)。

大家如果不信,可以用下列代码直接输出看看结果:

println!("{},{},{},{}",!(5 == 5),!(6 <= 4),!true,!false);

逻辑运算符&&和||用来计算两个表达式而获得一个结果值。它们分别对应逻辑运算中的与运算(AND)和或运算(OR)。它们的运算结果取决于两个操作数的关系,如表4-2所示。

表4-2  两个操作数的逻辑运(&&和||)

第一个操作数a

第二个操作数b

a && b结果

a || b结果

true

true

true

true

true

false

false

true

false

true

false

true

false

false

false

false

例如:

  • ( (5 == 5) && (3 > 6) )返回false ( true && false )。
  • ( (5 == 5) || (3 > 6))返回true ( true || false )。

大家如果不信,可以用下列代码直接输出看看结果:

println!("{},{}",( (5 == 5) && (3 > 6) ) ,( (5 == 5) || (3 > 6)));

4.1.6  位运算符

位运算符以比特位改写变量存储的数值,也就是改写变量值的二进制表示。Rust的位运算符如表4-3所示。

表4-3  Rust的位运算符

   

   

   

   

位与

&

若相同位都是1,则返回1;否则返回 0

(A & B) 结果为2

位或

|

若相同位只有一个是1,则返回1;否则返回 0

(A | B) 结果为3

异或

^

若相同位不相同,则返回1;否则返回 0

(A ^ B) 结果为1

位非

!

把位中的1换成0,0换成1

(!B) 结果为−4

左移

<<

操作数中的所有位向左移动指定位数,右边的位补 0

(A << 1) 结果为4

右移

>>

操作数中的所有位向右移动指定位数,左边的位补 0

(A >> 1) 结果为1

下面的范例演示上面提到的所有位运算符。

fn main() {let a:i32 = 2;     // 二进制表示为 0 0 0 0 0 0 1 0let b:i32 = 3;     // 二进制表示为 0 0 0 0 0 0 1 1let mut result:i32;result = a & b;println!("(a & b) => {} ",result);result = a | b;println!("(a | b) => {} ",result) ;result = a ^ b;println!("(a ^ b) => {} ",result);result = !b;println!("(!b) => {} ",result);result = a << b;println!("(a << b) => {}",result);result = a >> b;println!("(a >> b) => {}",result);}

输出结果如下:

(a & b) => 2
(a | b) => 3
(a ^ b) => 1
(!b) => -4
(a << b) => 16
(a >> b) => 0

4.1.7  变量类型转换运算符

变量类型转换运算符可以将一种类型的数据转换为另一种类型的数据。在Rust中,可以使用关键字as进行类型转换,as 运算符有点像C中的强制类型转换,区别在于,它只能用于原始类型(i32、i64、f32、f64、u8、u32、char等类型),并且它是安全的。注意,不同的数值类型是不能进行隐式转换的。比如:

let b: i64 = iNum;  //iNum是一个i32类型的变量

会出现编译错误,提示无法进行类型转换。这时可以使用as 进行转换,比如:

fn main() {let mut iNum:i32;     let mut b:i64;    iNum=100;b = iNum as i64;print!("{}",b);}

输出结果:100。

为什么as是安全的?尝试以下代码:

b = iNum as char;

编译器报错:

error[E0604]: only `u8` can be cast as `char`, not `i32`

可见在不相关的类型之间,Rust 会拒绝转换,这样避免了运行时错误。

4.1.8  运算符的优先级

当多个操作数组成复杂的表达式时,我们可能会疑惑哪个运算先被计算,哪个后被计算。例如以下表达式:

a = 5 + 7 % 2

我们可以怀疑它实际上表示:a = 5 + (7 % 2) 结果为6,还是 a = (5 + 7) % 2 结果为0?

正确答案为第一个,结果为6。每一个运算符都有一个固定的优先级,不仅是数学运算符(我们可能在学习数学的时候已经很了解它们的优先顺序了),所有在Rust中出现的运算符都有优先级。从最高级到最低级,运算符的优先级按表4-4排列。

表4-4  运算符的优先级

   

   

结合方向

1

一元操作符(!、&、&mut)

从左到右

2

二元操作符(*、/、%)

从左到右

3

二元操作符(+、−)

从左到右

4

位移计算(<<、>>)

从左到右

5

位操作(&)

从左到右

6

位操作(|)

从左到右

7

比较操作(==、!=、<、>、<=、>=)

需要括号

8

逻辑与(&&)

从左到右

9

逻辑或(||)

从左到右

10

赋值操作(=、+=、−=、/=、%=、|=、^=、<<=、>>=)

从右到左

以下是简单的示例:

fn main() {//二元计算操作println!("1 + 2 = {}", 1u32 + 2);println!("1 - 2 = {}", 1i32 - 2);//逻辑操作println!("true AND false is {}", true && false);println!("true OR false is {}", true || false);println!("NOT true is {}", !true);//位运算操作println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);println!("1 << 5 is {}", 1u32 << 5);println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);}

运行结果如下:

1 + 2 = 3
1 - 2 = -1
true AND false is false
true OR false is true
NOT true is false
0011 AND 0101 is 0001
0011 OR 0101 is 0111
0011 XOR 0101 is 0110
1 << 5 is 32
0x80 >> 2 is 0x20

所有这些运算符的优先级顺序可以通过使用一对圆括号“()”来控制,而且更易读懂,示例如下:

a = 5 + 7 % 2;

根据我们想要实现的计算不同,可以写成:

a = 5 + (7 % 2);

效果和a = 5 + 7 % 2;一样,因为%的优先级比+高,所以加不加括号没什么区别。如果要先计算5+7,则可以这样:

a = (5 + 7) % 2;

此时最终计算结果就不同了。所以如果想写一个复杂的表达式而不敢肯定各个运算的执行顺序,那么就加上括号。这样可以使代码更易读懂。

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

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

相关文章

深度学习02-pytorch-08-自动微分模块

​​​​​​​ 其实自动微分模块&#xff0c;就是求相当于机器学习中的线性回归损失函数的导数。就是求梯度。 反向传播的目的&#xff1a; 更新参数&#xff0c; 所以会使用到自动微分模块。 神经网络传输的数据都是 float32 类型。 案例1: 代码功能概述&#xff1a; 该…

分页插件、代码生成器

01-分页插件、代码生成器 分页插件使用 首先在pom.xml文件中导入依赖 然后再mybatis-config.xml文件中写入插件 在测试类中写入方法 在mybatis.xml文件中设置plugins标签里的属性helperDialectkeyi自动检查当前数据库用的什么,不用设置也行,默认就设置了 分页插件里面属性详解…

【ranger编译报错】cloudera-manager-api-swaggerjar7.0.3 not found

cloudera-manager-api-swaggerjar7.0.3 not found 快捷导航 在开始解决问题之前&#xff0c;大家可以通过下面的导航快速找到相关资源啦&#xff01;&#x1f4a1;&#x1f447; 快捷导航链接地址备注相关文档-ambaribigtop自定义组件集成https://blog.csdn.net/TTBIGDATA/a…

【在Linux世界中追寻伟大的One Piece】IP分片和组装的具体过程

目录 1 -> IP分片和组装的具体过程 2 -> 分片与组装的过程 2.1 -> 分片 2.2 -> 组装 3 -> 分片与组装的示意图 3.1 -> 分片组装场景 1 -> IP分片和组装的具体过程 16位标识(id)&#xff1a;唯一的标识主机发送的报文。如果IP报文在数据链路层被分片…

利士策分享,华为三折叠手机:重塑未来科技生活的里程碑

利士策分享&#xff0c;华为三折叠手机&#xff1a;重塑未来科技生活的里程碑 在这个日新月异的科技时代&#xff0c;华为再次以惊人的创新力&#xff0c;引领我们迈向智能设备的全新纪元——华为三折叠手机&#xff0c; 不仅是技术的飞跃&#xff0c;更是对未来生活方式的一次…

【保奖思路】2024年华为杯研赛B题完整代码建模过程(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 点击链接加入【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/XzdIsvbiM0https://qm.qq.com/q/XzdIsvbiM0请根据附件WLAN网络实测训练集中所提供…

代理IP对于网络爬虫业务的重要性

在现代互联网业务中&#xff0c;网络爬虫已成为企业获取大量数据的重要工具。在这一过程中&#xff0c;代理IP发挥着至关重要的作用&#xff0c;特别是对于高频率的数据抓取任务&#xff0c;代理IP能够有效提升爬虫的成功率和安全性。 1. 绕过IP封禁 网站为了防止过度的爬虫行…

TC-RAG: 图灵完备的检索增强

1. 背景 大型语言模型在众多关键领域均已取得显著进展&#xff0c;并在各种下游任务中展现出卓越性能。 在医疗领域&#xff0c;这些模型尤显潜力&#xff0c;特别是在对责任感和可靠性要求极高的健康护理领域。这些模型通过全面的医学知识预训练&#xff0c;不仅能支持医生做…

Python语法(二)——函数

文章目录 函数语法格式函数参数链式调用嵌套调用函数递归变量作用域 函数 一段可以被重复使用的代码片段 求数列的和, 不使用函数 # 1.求1-100的和 sum 0 for i in range(1, 101):sum i print(fsum {sum})# 2.求300-400的和 sum 0 for i in range(300, 401):sum i print(…

NASA:ATLAS/ICESat-2 L3A 陆地和植被高度速览,第 6 版

目录 简介 参数 代码 引用 网址推荐 0代码在线构建地图应用 机器学习 ATLAS/ICESat-2 L3A 陆地和植被高度速览&#xff0c;第 6 版 ATLAS/ICESat-2 L3A Land and Vegetation Height Quick Look V006 简介 ATL08QL 是 ATL08 的快速查看版本。 一旦 ATL08 的最终文件可…

MongoDB在Linux系统中的安装与配置指南

在这篇文章中&#xff0c;我们将介绍如何在CentOS 7服务器上安装MongoDB&#xff0c;并通过DataX将数据从MongoDB迁移到MySQL数据库。这将包括MongoDB的安装、配置、数据准备以及使用DataX进行数据迁移的详细步骤。 MongoDB简介 MongoDB是一个高性能、开源、无模式的文档型数据…

[Java并发编程] synchronized(含与ReentrantLock的区别)

文章目录 1. synchronized与ReentrantLock的区别2. synchronized的作用3. synchronized的使用3.1 修饰实例方法&#xff0c;作用于当前实例&#xff0c;进入同步代码前需要先获取实例的锁3.2 修饰静态方法&#xff0c;作用于类的Class对象&#xff0c;进入修饰的静态方法前需要…

数据结构:二叉树(2)

ps&#xff1a;爆更第二期 前言 普通的树的实用价值比较小&#xff0c;将树更一步特殊化成二叉树&#xff0c;将获得更多的特殊性质。 例如搜索二叉树&#xff0c;红黑树等。 这篇博文主要介绍二叉树的基础知识&#xff0c;进阶版高级二叉树&#xff0c;后续会持续更新。 二叉…

RK3568平台(基础篇)万用表的使用

一.万用表的通断判断 万用表两个笔头的插法:黑笔头是插在COM的孔里面,红色笔头可以插在其他的三个孔里面,20A和mA分别用来测电流,另外一个孔可以用来测其他(电压 电阻)。 以下这个三角符号(像wifi一样的)可以用来测通断: 使用万用表的红笔和黑笔进行短接,这时候两端…

PAT (Advanced Level) Practice——1020Tree Traversals

链接&#xff1a; 1020 Tree Traversals - PAT (Advanced Level) Practice (pintia.cn) 题目大意&#xff1a; 首先给出一个整数n&#xff0c;表示序列一共有多少个数。接下来给出一棵树的后序遍历和中序遍历&#xff0c;根据后序遍历和中序遍历给出层序遍历。 题解&#x…

【技术调研】三维(7)-Unity基础笔记

安装 ​ 最好使用长期维护版本。 创建项目 ​ 略 窗口布局 Hierarchy:层级面板,展示当前打开的场景里面有哪些物体。 Scene:场景面板,显示当前场景的样子 Game:游戏面板,场景运行的时候的样子 Inspector:检视面板(或属性面板),查看一个游戏物体由哪些组件组成。 …

德勤校招网申笔试综合能力测试SHL题库与面试真题攻略

德勤的综合能力测试&#xff08;General Ability&#xff09;是其校园招聘在线测评的关键环节&#xff0c;旨在评估应聘者的多项认知能力。以下是对这部分内容的全面整合&#xff1a; 综合能力测试&#xff08;General Ability&#xff09; 测试时长为46分钟&#xff0c;包含…

9.3Otsu阈值分割

基本概念 在OpenCV中&#xff0c;Otsu阈值分割是一种全局阈值分割方法&#xff0c;但它会自动选择一个最佳的阈值来分割图像&#xff0c;这个阈值是通过最小化类内方差或等价地最大化类间方差来确定的。OpenCV提供了cv::threshold函数来实现这一功能&#xff0c;其中可以指定c…

线段树-认识线段树+实现线段树

一、认识线段树 1、定义 线段树是平衡二叉树 2、特点 线段树将一个区间划分成单元区间&#xff0c;每个单元区间对应线段树中的一个结点 3、应用 频繁查找一个数组中指定区间内的和、最值 学了动态规划后使用迭代要好过使用递归&#xff0c;因为递归每次进去是有空间损耗…

如何在qtcreator debugger上运行gdb命令

How to run gdb commands from qtcreator debugger? | Qt Forum gdb 调试基础操作和在qtcreator中使用gdb调试_qt gdb-CSDN博客 输出变量名&#xff1a; p变量名 ------------ gdb调试技巧&#xff08;二&#xff09;———— gdb 条件断点_gdb设置带函数入参判断的条件断点…